From ec92ee12da5882c3ac3f3d21240c381cb4619883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Alvial?= Date: Wed, 29 Apr 2026 10:35:08 -0300 Subject: [PATCH 1/5] docs(api): comprehensive consistency audit pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sweeps the three OpenAPI specs (fx-account, fx-payment, fx-webhook), the guide pages, and the journey docs to align them with each other and with the live source. Most schema/wire-format changes need backend follow-up (see list below) — the docs now describe the target state. # Highlights ## Pagination unified - Single `PageMetadata` schema across all three specs (fields: `previousCursor`, `nextCursor`, `total`) - All list endpoints expose `limit`, `cursor`, `direction`, `sortOrder`, `filters` parameters - `listUbos` and `listFundingInstructions` wrapped in `{data, meta}` - fx-payment default limit aligned to 10 - `totalMatches` and `PaginationIncludeTotalMatches` removed (per direction to drop these from the public surface) ## Money shape switched to decimal strings everywhere - `Amount.value` is now a decimal string in the asset's canonical scale - Dropped the `decimalValue` field (single source of truth) - Aligned to the industry pattern used by Coinbase, PayPal, Circle, Binance for hybrid fiat+crypto APIs - `money.mdx` rewritten + `BigDecimal/Decimal` library guidance ## Asset enum unified - One `Asset` enum with 20 values across all three specs (was three separate enums with 7 / 16 / 20 values, two named `Currency` and one named `Asset`) ## Idempotency - `X-Idempotency-Key` is `required: true` in all three specs (was only required in fx-payment) - Added the parameter to `updateUbo` PATCH (was missing entirely) ## Terminology sweep - "partner" → "customer" across webhook spec descriptions, account spec, subscribe.mdx (per the rule in CLAUDE.md) - "users" → "account owners" in index.mdx and environments.mdx - `FeeSource` enum value renamed `PARTNER` → `CUSTOMER` - `INSUFFICIENT_OWNERSHIP` error message rephrased - `CONTRIBUTING.md` removed (rules consolidated into CLAUDE.md) ## Webhook event types - All event type strings standardized to `dot.lowercase` per the Stripe / Svix industry convention - Beneficiary events renamed `beneficiary.paymentInstruction.*` (was bare `instruction`) - Operation event scope explicitly limited to creation only (lifecycle transitions tracked via polling — documented in journeys and overview) - `EventType` enum values updated; MDX files renamed; docs.json updated - `atTime` semantics documented (distinct from resource `createdAt`) ## TaxId aligned across services - Beneficiary `TaxId` shape switched from `{type, number}` to `{value, type, country}` to match account-owner shape - Split into `TaxIdRequest` / `TaxIdResponse` schemas ## State.reason cleanup - `OperationState.reason` and `InstructionState.reason` no longer required (and no longer nullable); fields are simply omitted on success/non-terminal states ## Withdrawal journey example fixed - PIX field names corrected (`dictKey/dictKeyType`, not `pixKey/pixKeyType`) - TaxId nested as object, not bare string - Required `relationshipType` added - PIX `asset` field added ## Cross-cutting docs improvements - `X-Request-Id` header documented on every response (header component + ~92 inline references) - 401 UnauthorizedError responses added to all secured fx-webhook ops - `readOnly: true` on `id`, `createdAt`, `updatedAt`, `status` in response schemas (better SDK codegen) - Datetime examples aligned to live API format (no `.000Z` suffix) - "Auth0 JWT" replaced with provider-agnostic wording in security scheme descriptions - OpenAPI version unified to 3.1.1 - `DepositFundingInstructionInput` renamed to `DepositFundingInstructionRequest` - `filters` LHS-bracket syntax now exposed on every list endpoint per the filtering principle - 5 unused snippets deleted (only `money-format` retained) # Backend follow-ups required The docs are ahead of the live API on: 1. Money: `value` becomes decimal string, `decimalValue` field removed 2. `X-Idempotency-Key` enforced on all POST/PUT/PATCH (currently only fx-payment) 3. `FeeSource` emits `CUSTOMER` instead of `PARTNER` 4. `INSUFFICIENT_OWNERSHIP` error message text 5. `X-Event-Type` header emits dot.lowercase 6. Beneficiary events renamed `beneficiary.paymentInstruction.*` 7. `*State.reason` omitted on success (no longer `null`) 8. `direction`/`sortOrder`/`filters` parsing on listOperations, listBeneficiaries, listUbos, listFundingInstructions 9. `{data, meta}` envelope on listUbos and listFundingInstructions 10. Beneficiary TaxId becomes `{value, type, country}` 11. Asset enum unified to 20 values across all services 12. `filters` LHS-bracket parser everywhere 13. `X-Request-Id` response header on every response 14. `PaginationIncludeTotalMatches` / `totalMatches` removed from public surface --- .claude/rules/openapi.md | 4 +- CLAUDE.md | 20 +- CONTRIBUTING.md | 150 -------- .../beneficiary-instruction-created.mdx | 5 - ...eficiary-payment-instruction-approved.mdx} | 4 +- ...eneficiary-payment-instruction-created.mdx | 5 + ...eficiary-payment-instruction-rejected.mdx} | 4 +- apis/fx-account/openapi.yml | 301 ++++++++++++---- apis/fx-payment/openapi.yml | 337 +++++++++++++----- apis/fx-webhook/openapi.yml | 232 +++++++++--- docs.json | 6 +- guides/environments.mdx | 2 +- guides/principles/datetime.mdx | 6 +- guides/principles/money.mdx | 54 +-- index.mdx | 2 +- journeys/deposit.mdx | 2 +- journeys/swap.mdx | 2 +- journeys/withdrawal.mdx | 14 +- snippets/auth-header.mdx | 7 - snippets/error-response.mdx | 11 - snippets/idempotency-note.mdx | 5 - snippets/money-format.mdx | 9 +- snippets/pagination-response.mdx | 14 - snippets/version-header.mdx | 5 - webhooks/overview.mdx | 16 +- webhooks/subscribe.mdx | 6 +- webhooks/verify-signatures.mdx | 2 +- 27 files changed, 748 insertions(+), 477 deletions(-) delete mode 100644 CONTRIBUTING.md delete mode 100644 api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-created.mdx rename api-reference/fx-webhook/events/beneficiary/{beneficiary-instruction-approved.mdx => beneficiary-payment-instruction-approved.mdx} (52%) create mode 100644 api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created.mdx rename api-reference/fx-webhook/events/beneficiary/{beneficiary-instruction-rejected.mdx => beneficiary-payment-instruction-rejected.mdx} (52%) delete mode 100644 snippets/auth-header.mdx delete mode 100644 snippets/error-response.mdx delete mode 100644 snippets/idempotency-note.mdx delete mode 100644 snippets/pagination-response.mdx delete mode 100644 snippets/version-header.mdx diff --git a/.claude/rules/openapi.md b/.claude/rules/openapi.md index 0d141b7..3e0de5e 100644 --- a/.claude/rules/openapi.md +++ b/.claude/rules/openapi.md @@ -37,7 +37,7 @@ machine-checkable subset. `components.responses` and are referenced via `$ref`. Example: `WebhookAck` for the standard 200 response on every webhook event. - Standard schemas that appear in every spec: `Currency`, `Rail`, - `ErrorResponse`, `PaginationMeta`. Always named components, never + `ErrorResponse`, `PageMetadata`. Always named components, never inline. ## Discriminated unions @@ -88,7 +88,7 @@ machine-checkable subset. - List endpoints reference the shared parameters: `PaginationLimit`, `PaginationCursor`, `PaginationDirection`, `PaginationSortOrder`. -- Response wraps as `{ data: [...], meta: PaginationMeta }`. The schema +- Response wraps as `{ data: [...], meta: PageMetadata }`. The schema name is `XxxList`. ## Description style diff --git a/CLAUDE.md b/CLAUDE.md index 8e19539..11daf33 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What this repo is -Trace Finance's public developer documentation for the FX platform, built on [Mintlify](https://mintlify.com). Partners ("customers") integrate with two services — **FX Account** (multi-currency account management) and **FX Payment** (deposits, withdrawals, swaps, beneficiaries) — documented here as one unified API surface. +Trace Finance's public developer documentation for the FX platform, built on [Mintlify](https://mintlify.com). Customers integrate with **FX Account** (multi-currency account management), **FX Payment** (deposits, withdrawals, swaps, beneficiaries), and **FX Webhook** (subscriptions and event delivery) — documented here as one unified API surface. Content is public. Internal class names, backoffice endpoints, dashboard endpoints, and ADRs never appear here. @@ -35,23 +35,23 @@ Pages are MDX with YAML frontmatter (`title` + `description` required). Mintlify | `journeys/` | End-to-end flows: open accounts, deposit, withdraw, swap | | `webhooks/` | Webhook setup and event catalog | | `snippets/` | Reusable MDX fragments imported into other pages | -| `apis/` | OpenAPI specs (scaffold) — `fx-account/openapi.yml` and `fx-payment/openapi.yml` | +| `apis/` | OpenAPI specs — `fx-account/openapi.yml`, `fx-payment/openapi.yml`, `fx-webhook/openapi.yml` | -### API Reference (OpenAPI) — deferred +### API Reference (OpenAPI) -OpenAPI specs live in `apis/` but are not yet wired into `docs.json` navigation. When schemas are ready, add the `openapi` key and an API Reference tab to `docs.json`. Endpoint pages auto-generate from `apis/{service}/openapi.yml`. Only `/api` channel endpoints are documented. `/dashboard` and `/admin` are internal. +Endpoint pages auto-generate from `apis/{service}/openapi.yml`. Only `/api` channel endpoints are documented. `/dashboard` and `/admin` are internal. ## Terminology - **Customer** = the company integrating with Trace FX. - **Account owner** = the person whose account is managed (maps to `account.owner`). -- Never use "partner" or "client." +- Never use "partner", "client", or "user" in public content. -## Key rules (full rulebook in CONTRIBUTING.md) +## Key rules - Internal links: root-relative, no extension (`/guides/authentication`). - No internal class names in public content (`AmountV2` → "amount object"). -- Money: minor units as integers (`{ "value": 500000, "asset": "BRL" }`). +- Money: decimal strings in the asset's canonical scale (`{ "value": "5000.00", "asset": "BRL", "decimals": 2 }`). Never integer minor units in the wire format. Never JS `Number` for parsing — use `BigDecimal`/`Decimal`. - Code blocks always declare language. - Sentence case headings; no marketing adjectives; no filler. - OpenAPI specs live here — when service teams change `/api` endpoints, they PR the spec update here too. @@ -107,11 +107,11 @@ Page types are inferred from file paths: `snippets/` contains shared MDX blocks. Import them in any page: ```mdx -import AuthHeader from '/snippets/auth-header.mdx'; - +import MoneyFormat from '/snippets/money-format.mdx'; + ``` -Available: `auth-header`, `idempotency-note`, `pagination-response`, `error-response`, `money-format`, `version-header`. +Currently `money-format` is the only snippet. Add new ones only when the same prose appears verbatim on three or more pages. ### CI (GitHub Actions) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 857902f..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,150 +0,0 @@ -# Contributing to Trace Finance Developer Docs - -Rules and conventions for anyone editing this documentation site. - -## Audience - -These docs are **public** and serve **customers** — the companies integrating with Trace FX. Do not publish internal implementation details, backoffice endpoints, or dashboard-specific content. - -## Terminology - -| Term | Meaning | Usage | -|---|---|---| -| **Customer** | The company integrating with Trace FX | "As a customer, you authenticate using your API credentials." | -| **Account owner** | The person or entity whose account is being managed (maps to `account.owner` in the API) | "Create an account for an account owner." | - -Never use "customer" to mean the account owner. Never use "partner" or "client" — use "customer." - -## Content ownership - -| Content | Location | Owner | When to update | -|---|---|---|---| -| Endpoint reference (OpenAPI) | `apis/{service}/openapi.yml` | Service team | Every `/api` endpoint change | -| Everything else (MDX) | `guides/`, `journeys/`, `webhooks/`, `reference/`, `snippets/` | Whoever writes docs | Product or conceptual changes | - -## Drift prevention - -OpenAPI specs live in this repo, not in the service repos. To prevent drift: - -1. **PR checklist in service repos**: if a PR changes an `/api` endpoint, the author opens a follow-up PR here updating `apis/{service}/openapi.yml`. -2. **CODEOWNERS**: `apis/fx-account/openapi.yml` and `apis/fx-payment/openapi.yml` require review from the respective backend team. -3. **Quarterly audit**: verify that deployed endpoints still match the committed specs. - -## Scope: `/api` channel only - -Both services expose three channels (`/api`, `/dashboard`, `/admin`). Only `/api` endpoints appear in these docs. Strip `/dashboard` and `/admin` paths from OpenAPI specs before committing. - -## File naming - -- Kebab-case: `open-brl-account.mdx`, not `openBrlAccount.mdx`. -- No date prefixes or version numbers: `deposit.mdx`, not `01-deposit.mdx`. -- The filename becomes the URL path — keep it readable. - -## Frontmatter - -Every MDX file requires `title` and `description`: - -```yaml ---- -title: "Authenticate requests" -description: "How to obtain and use Auth0 JWTs to call Trace FX APIs." ---- -``` - -Optional fields: -- `sidebarTitle`: when `title` would wrap in the sidebar. -- `icon`: for top-level group landing pages only. -- `tag: "BETA"`: for unreleased endpoints or features. - -## Internal links - -Root-relative, no file extension: - -```mdx -[see authentication](/guides/authentication) -``` - -Never use relative paths (`../`) or absolute external URLs for internal pages. - -## Voice and style - -- Second-person ("you"), active voice. -- Sentence case for headings. -- Bold for UI elements (**Dashboard**). -- `code` formatting for filenames, commands, headers (`X-Idempotency-Key`), and endpoint paths (`POST /accounts`). -- No marketing adjectives (*powerful, seamless, robust*). -- No filler phrases (*in order to, it's important to note*). -- No editorializing (*simply, just, obviously*). -- Internal class names (`AmountV2`, `ApplicationException`, `DefaultClaims`) **never appear** in public content. Use the field name as customers see it: "amount object", "error response." - -## Components - -Use Mintlify built-in components. Do not create custom components. - -| Need | Use | -|---|---| -| Step-by-step instructions | `` | -| Mutually exclusive examples | `` | -| Optional deep detail | `` / `` | -| Multi-language code | `` | -| Callouts | ``, ``, ``, ``, `` | -| Cross-page navigation | `` | -| API parameter (MDX-only reference) | `` | - -## Code examples - -- Use realistic values: actual currency codes (`BRL`, `USD`), realistic UUIDs, sandbox base URL (`https://faas.sandbox.tracefinance.io`). -- Every code block must declare its language: ` ```json `, ` ```bash `, etc. -- Money uses minor units: `{ "value": 500000, "asset": "BRL" }` not `{ "amount": "5.00 BRL" }`. -- No `foo`, `bar`, `test123`, or placeholder values. - -## Reusable content - -Shared fragments live in `snippets/`. Import them in MDX: - -```mdx -import AuthHeader from '/snippets/auth-header.mdx'; - - -``` - -Only create a snippet when the exact same content appears on multiple pages. Do not snippet content that varies between pages. - -## Adding a new endpoint - -1. Add the operation to `apis/{service}/openapi.yml`. -2. Add the operation path to the relevant group in `docs.json` (e.g., `"POST /api/operations/withdrawal"`). -3. Run `mint dev` — verify the endpoint renders with a playground. -4. Run `mint broken-links`. - -## Adding a new guide page - -1. Create the MDX file at the appropriate path (e.g., `guides/topic.mdx`). -2. Add the path (without `.mdx`) to the appropriate group in `docs.json`. -3. Include `title` and `description` frontmatter. -4. Cross-link from at least one existing page — orphan pages are hard to find. -5. Run `mint dev` and `mint broken-links`. - -## Validation before merge - -Every PR to `main` must pass: - -- `mint dev` renders changed pages without errors. -- `mint broken-links` passes. -- `mint validate` passes. -- OpenAPI changes validate as OpenAPI 3.x. - -## `.mintignore` - -Drafts go in `drafts/` or use `*.draft.mdx`. Use `.mintignore` to exclude files from builds entirely. Do not rely on "not in docs.json" — Mintlify can still index unlisted pages for search. - -## Images - -Store in `/images/{topic}/`. Always include descriptive alt text that says what the image *conveys*, not what it *is*. - -Provide light and dark variants when images have white backgrounds: - -```mdx -... -... -``` diff --git a/api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-created.mdx b/api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-created.mdx deleted file mode 100644 index 27a0768..0000000 --- a/api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-created.mdx +++ /dev/null @@ -1,5 +0,0 @@ ---- -title: "beneficiary.instruction.created" -description: "Fires when a payment instruction is added to a beneficiary; the new instruction starts in PENDING_REVIEW." -openapi: "apis/fx-webhook/openapi.yml webhook beneficiary.instruction.created" ---- diff --git a/api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-approved.mdx b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-approved.mdx similarity index 52% rename from api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-approved.mdx rename to api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-approved.mdx index df3d3ba..6bf9d19 100644 --- a/api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-approved.mdx +++ b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-approved.mdx @@ -1,5 +1,5 @@ --- -title: "beneficiary.instruction.approved" +title: "beneficiary.paymentInstruction.approved" description: "Fires when a payment instruction on a beneficiary is approved; check instructions[].status to find the one that transitioned." -openapi: "apis/fx-webhook/openapi.yml webhook beneficiary.instruction.approved" +openapi: "apis/fx-webhook/openapi.yml webhook beneficiary.paymentInstruction.approved" --- diff --git a/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created.mdx b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created.mdx new file mode 100644 index 0000000..fedee9f --- /dev/null +++ b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created.mdx @@ -0,0 +1,5 @@ +--- +title: "beneficiary.paymentInstruction.created" +description: "Fires when a payment instruction is added to a beneficiary; the new instruction starts in PENDING_REVIEW." +openapi: "apis/fx-webhook/openapi.yml webhook beneficiary.paymentInstruction.created" +--- diff --git a/api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-rejected.mdx b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected.mdx similarity index 52% rename from api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-rejected.mdx rename to api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected.mdx index 93066c5..ec27716 100644 --- a/api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-rejected.mdx +++ b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected.mdx @@ -1,5 +1,5 @@ --- -title: "beneficiary.instruction.rejected" +title: "beneficiary.paymentInstruction.rejected" description: "Fires when a payment instruction on a beneficiary is rejected; check instructions[].status to find the one that transitioned." -openapi: "apis/fx-webhook/openapi.yml webhook beneficiary.instruction.rejected" +openapi: "apis/fx-webhook/openapi.yml webhook beneficiary.paymentInstruction.rejected" --- diff --git a/apis/fx-account/openapi.yml b/apis/fx-account/openapi.yml index 410d7b9..6b24d24 100644 --- a/apis/fx-account/openapi.yml +++ b/apis/fx-account/openapi.yml @@ -1,4 +1,4 @@ -openapi: 3.1.0 +openapi: 3.1.1 info: title: FX Account API version: 1.0.0 @@ -27,7 +27,13 @@ components: type: http scheme: bearer bearerFormat: JWT - description: "Auth0 JWT token. Include as `Authorization: Bearer `." + description: "JWT bearer token. Include as `Authorization: Bearer `. See the [Authentication](/guides/authentication) guide for how to obtain one." + headers: + RequestId: + description: Unique request identifier emitted on every response. Reference it when contacting Trace support so we can trace the request end-to-end. See [Errors](/guides/principles/errors). + schema: + type: string + format: uuid parameters: AccountId: name: accountId @@ -56,8 +62,8 @@ components: IdempotencyKey: name: X-Idempotency-Key in: header - required: false - description: Unique key to ensure idempotent request processing. + required: true + description: Unique key to ensure idempotent request processing. Required on all `POST`, `PUT`, and `PATCH` requests. schema: type: string format: uuid @@ -99,14 +105,17 @@ components: - ASCENDING - DESCENDING default: DESCENDING - PaginationIncludeTotalMatches: - name: includeTotalMatches + Filters: + name: filters in: query required: false - description: Whether to include the total number of matching records in the response metadata. + description: | + Filter expression using LHS Brackets syntax (`field[operator]=value`). + Combine multiple conditions with `and(...)`, `or(...)`, or `;` (implicit AND). + See the [Filtering](/guides/principles/filtering) guide for the full operator catalog and examples. schema: - type: boolean - default: false + type: string + example: "states.status[last]=ACTIVE" schemas: # ── Request schemas ────────────────────────────────────────────── CreateAccountRequest: @@ -128,7 +137,7 @@ components: type: object properties: code: - $ref: "#/components/schemas/Currency" + $ref: "#/components/schemas/Asset" isVirtual: type: boolean description: Whether this is a virtual (non-custodial) asset. @@ -333,6 +342,7 @@ components: type: string format: uuid example: "a1b2c3d4-5e6f-7890-abcd-ef1234567890" + readOnly: true sourceAccountId: type: string format: uuid @@ -365,10 +375,12 @@ components: type: string format: date-time example: "2026-01-15T10:30:00Z" + readOnly: true updatedAt: type: string format: date-time example: "2026-01-15T10:30:00Z" + readOnly: true required: - id - customer @@ -384,8 +396,8 @@ components: type: object description: An asset available on the account and the onboarding status of its underlying provider account. properties: - currency: - $ref: "#/components/schemas/Currency" + asset: + $ref: "#/components/schemas/Asset" allowsVirtualAccounts: type: boolean description: Whether virtual accounts can be issued for this asset. @@ -400,42 +412,38 @@ components: - FROZEN - DEACTIVATED example: "ONBOARDING" + readOnly: true required: - - currency + - asset - allowsVirtualAccounts - status AmountResponse: type: object description: > - Monetary amount returned by the API. Carries both an integer (for - arithmetic) and a decimal string (for display) so callers don't need to - know per-currency precision upfront. + Monetary amount expressed as a decimal string in the asset's canonical + scale. Use a decimal-precision library (`BigDecimal`, `Decimal`) for + arithmetic — never JavaScript `Number`. properties: value: - type: integer - description: Amount in minor units of `currency` (e.g., 50000 = 500.00 BRL). - example: 500000 - decimalValue: type: string pattern: '^-?\d+(\.\d+)?$' - description: Decimal representation in the currency's canonical scale. + description: Decimal amount in the asset's canonical scale. example: "5000.00" - currency: - $ref: "#/components/schemas/Currency" + asset: + $ref: "#/components/schemas/Asset" decimals: type: integer - description: Number of fractional digits used to render `value` as `decimalValue`. + description: Number of decimal places for the asset. example: 2 required: - value - - decimalValue - - currency + - asset - decimals BalanceEntryResponse: type: object description: > - A single balance entry for one currency. When `synced` is `false`, the - upstream provider was unreachable and `amount` is omitted; partners + A single balance entry for one asset. When `synced` is `false`, the + upstream provider was unreachable and `amount` is omitted; the customer should retry or fall back to a previously cached value. properties: amount: @@ -479,6 +487,7 @@ components: type: string description: Customer identifier. example: "c1a2b3c4-d5e6-7890-abcd-ef1234567890" + readOnly: true required: - id OwnerResponse: @@ -783,9 +792,9 @@ components: enum: - IDENTITY_VERIFICATION example: "IDENTITY_VERIFICATION" - Currency: + Asset: type: string - description: Fiat or crypto currency code. + description: ISO 4217 currency code or stablecoin ticker. enum: - USD - BRL @@ -1145,6 +1154,7 @@ components: type: string format: uuid example: "b2c3d4e5-f6a7-8901-bcde-f12345678901" + readOnly: true name: type: string example: "João Silva" @@ -1200,7 +1210,7 @@ components: type: object properties: asset: - $ref: "#/components/schemas/Currency" + $ref: "#/components/schemas/Asset" rail: $ref: "#/components/schemas/Rail" required: @@ -1382,26 +1392,26 @@ components: - referenceId PageMetadata: type: object + description: Cursor-based pagination metadata. properties: - previous: + previousCursor: type: string nullable: true - description: Cursor for the previous page. - next: + description: Cursor for the previous page. `null` on the first page. + nextCursor: type: string nullable: true - description: Cursor for the next page. - count: + description: Cursor for the next page. `null` on the last page. + total: type: integer description: Number of items in the current page. - totalMatches: - type: integer - nullable: true - description: Total number of matching records. Only present when `includeTotalMatches` is true. required: - - count + - previousCursor + - nextCursor + - total ErrorResponse: type: object + description: Standard error envelope. The HTTP response also carries an `X-Request-Id` header — reference it when contacting Trace support. See [Errors](/guides/principles/errors). properties: code: type: string @@ -1418,6 +1428,9 @@ components: responses: UnauthorizedError: description: Missing or invalid authentication token. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1485,16 +1498,14 @@ components: accountId: "5b7c1f2a-3d4e-4f8a-9b1c-2d3e4f5a6b7c" fiat: - amount: - value: 500000 - decimalValue: "5000.00" - currency: "BRL" + value: "5000.00" + asset: "BRL" decimals: 2 synced: true crypto: - amount: - value: 1500000 - decimalValue: "1.500000" - currency: "USDC" + value: "1.500000" + asset: "USDC" decimals: 6 synced: true BalanceUnsyncedSource: @@ -1503,9 +1514,8 @@ components: accountId: "5b7c1f2a-3d4e-4f8a-9b1c-2d3e4f5a6b7c" fiat: - amount: - value: 500000 - decimalValue: "5000.00" - currency: "BRL" + value: "5000.00" + asset: "BRL" decimals: 2 synced: true - amount: null @@ -1577,12 +1587,18 @@ paths: responses: "201": description: Account created. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/AccountResponse" "400": description: Invalid request. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1621,6 +1637,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Customer not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1637,6 +1656,9 @@ paths: id: "c1a2b3c4-d5e6-7890-abcd-ef1234567890" "422": description: Business rule violation. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1661,10 +1683,13 @@ paths: - $ref: "#/components/parameters/PaginationCursor" - $ref: "#/components/parameters/PaginationDirection" - $ref: "#/components/parameters/PaginationSortOrder" - - $ref: "#/components/parameters/PaginationIncludeTotalMatches" + - $ref: "#/components/parameters/Filters" responses: "200": description: Paginated list of accounts. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1695,12 +1720,18 @@ paths: responses: "200": description: Account details. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/AccountResponse" "400": description: Invalid account ID format. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1712,6 +1743,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Account not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1734,10 +1768,16 @@ paths: responses: "202": description: Account submitted for review. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" "401": $ref: "#/components/responses/UnauthorizedError" "404": description: Account not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1747,6 +1787,9 @@ paths: $ref: "#/components/examples/AccountNotFound" "409": description: Account state conflict. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1768,6 +1811,9 @@ paths: accountId: "5b7c1f2a-3d4e-4f8a-9b1c-2d3e4f5a6b7c" "422": description: Business rule violation. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1804,31 +1850,55 @@ paths: parameters: - $ref: "#/components/parameters/AccountId" - $ref: "#/components/parameters/TraceVersion" + - $ref: "#/components/parameters/PaginationLimit" + - $ref: "#/components/parameters/PaginationCursor" + - $ref: "#/components/parameters/PaginationDirection" + - $ref: "#/components/parameters/PaginationSortOrder" + - $ref: "#/components/parameters/Filters" responses: "200": - description: List of funding instructions. + description: Paginated list of funding instructions. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: - type: array - items: - $ref: "#/components/schemas/FundingInstructionResponse" + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/FundingInstructionResponse" + meta: + $ref: "#/components/schemas/PageMetadata" + required: + - data + - meta examples: mixed: summary: PIX and TED instructions value: - - asset: "BRL" - rail: "PIX_KEY" - keyType: "CNPJ" - key: "12345678000101" - - asset: "BRL" - rail: "TED" - bankCode: "001" - branch: "0001" - accountNumber: "123456-7" - accountType: "CHECKING" + data: + - asset: "BRL" + rail: "PIX_KEY" + keyType: "CNPJ" + key: "12345678000101" + - asset: "BRL" + rail: "TED" + bankCode: "001" + branch: "0001" + accountNumber: "123456-7" + accountType: "CHECKING" + meta: + previousCursor: null + nextCursor: null + total: 2 "400": description: Invalid account ID format. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1840,6 +1910,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Account not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1869,6 +1942,9 @@ paths: responses: "200": description: Account balance. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1880,6 +1956,9 @@ paths: $ref: "#/components/examples/BalanceUnsyncedSource" "400": description: Invalid account ID format. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1891,6 +1970,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: No balance found for the given account. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1957,8 +2039,14 @@ paths: responses: "204": description: Document uploaded. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" "400": description: Invalid request or missing multipart fields. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2008,6 +2096,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Account or beneficial owner not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2055,12 +2146,18 @@ paths: responses: "201": description: Beneficial owner added. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/UBOResponse" "400": description: Invalid request. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2092,6 +2189,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Account not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2101,6 +2201,9 @@ paths: $ref: "#/components/examples/AccountNotFound" "409": description: UBO already exists. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2115,6 +2218,9 @@ paths: taxId: "12345678900" "422": description: Business rule violation. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2138,7 +2244,7 @@ paths: summary: UBO ownership percentage is below the 25% threshold value: code: "INSUFFICIENT_OWNERSHIP" - message: "Partner ownership percentage must be at least 25%" + message: "Beneficial owner ownership percentage must be at least 25%" details: taxId: "12345678900" ownershipPercentage: 10.0 @@ -2151,19 +2257,38 @@ paths: parameters: - $ref: "#/components/parameters/AccountId" - $ref: "#/components/parameters/TraceVersion" + - $ref: "#/components/parameters/PaginationLimit" + - $ref: "#/components/parameters/PaginationCursor" + - $ref: "#/components/parameters/PaginationDirection" + - $ref: "#/components/parameters/PaginationSortOrder" + - $ref: "#/components/parameters/Filters" responses: "200": - description: List of beneficial owners. + description: Paginated list of beneficial owners. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: - type: array - items: - $ref: "#/components/schemas/UBOResponse" + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/UBOResponse" + meta: + $ref: "#/components/schemas/PageMetadata" + required: + - data + - meta "401": $ref: "#/components/responses/UnauthorizedError" "404": description: Account not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2173,6 +2298,9 @@ paths: $ref: "#/components/examples/AccountNotFound" "422": description: Business rule violation. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2200,6 +2328,9 @@ paths: responses: "200": description: Beneficial owner details. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2208,6 +2339,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Account or beneficial owner not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2219,6 +2353,9 @@ paths: $ref: "#/components/examples/UboNotFound" "422": description: Business rule violation. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2240,6 +2377,7 @@ paths: parameters: - $ref: "#/components/parameters/AccountId" - $ref: "#/components/parameters/UboId" + - $ref: "#/components/parameters/IdempotencyKey" - $ref: "#/components/parameters/TraceVersion" requestBody: required: true @@ -2270,12 +2408,18 @@ paths: responses: "200": description: Beneficial owner updated. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/UBOResponse" "400": description: Invalid request. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2307,6 +2451,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Account or beneficial owner not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2318,6 +2465,9 @@ paths: $ref: "#/components/examples/UboNotFound" "422": description: Business rule violation. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2348,7 +2498,7 @@ paths: summary: UBO ownership percentage is below the 25% threshold value: code: "INSUFFICIENT_OWNERSHIP" - message: "Partner ownership percentage must be at least 25%" + message: "Beneficial owner ownership percentage must be at least 25%" details: taxId: "12345678900" ownershipPercentage: 10.0 @@ -2365,10 +2515,16 @@ paths: responses: "204": description: Beneficial owner removed. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" "401": $ref: "#/components/responses/UnauthorizedError" "404": description: Account or beneficial owner not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2380,6 +2536,9 @@ paths: $ref: "#/components/examples/UboNotFound" "422": description: Business rule violation. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: diff --git a/apis/fx-payment/openapi.yml b/apis/fx-payment/openapi.yml index 525b6c5..8393b17 100644 --- a/apis/fx-payment/openapi.yml +++ b/apis/fx-payment/openapi.yml @@ -25,7 +25,13 @@ components: type: http scheme: bearer bearerFormat: JWT - description: "Auth0 JWT token. Include as `Authorization: Bearer `." + description: "JWT bearer token. Include as `Authorization: Bearer `. See the [Authentication](/guides/authentication) guide for how to obtain one." + headers: + RequestId: + description: Unique request identifier emitted on every response. Reference it when contacting Trace support so we can trace the request end-to-end. See [Errors](/guides/principles/errors). + schema: + type: string + format: uuid parameters: OperationId: name: operationId @@ -63,7 +69,7 @@ components: name: X-Idempotency-Key in: header required: true - description: Unique key to ensure idempotent request processing. + description: Unique key to ensure idempotent request processing. Required on all `POST`, `PUT`, and `PATCH` requests. schema: type: string format: uuid @@ -71,19 +77,51 @@ components: name: limit in: query required: false - description: Maximum number of items to return. Defaults to 20. + description: Maximum number of items to return. Defaults to 10. schema: type: integer - default: 20 + default: 10 minimum: 1 maximum: 100 PaginationCursor: name: cursor in: query required: false - description: Opaque cursor for fetching the next page. + description: Opaque cursor for fetching the next or previous page. + schema: + type: string + PaginationDirection: + name: direction + in: query + required: false + description: Direction of pagination relative to the cursor. + schema: + type: string + enum: + - NEXT + - PREVIOUS + PaginationSortOrder: + name: sortOrder + in: query + required: false + description: Sort order for results. Defaults to DESCENDING. + schema: + type: string + enum: + - ASCENDING + - DESCENDING + default: DESCENDING + Filters: + name: filters + in: query + required: false + description: | + Filter expression using LHS Brackets syntax (`field[operator]=value`). + Combine multiple conditions with `and(...)`, `or(...)`, or `;` (implicit AND). + See the [Filtering](/guides/principles/filtering) guide for the full operator catalog and examples. schema: type: string + example: "intent.type[eq]=WITHDRAWAL;currentState.status[eq]=COMPLETED" schemas: # ── Shared value objects ────────────────────────────────────────── Asset: @@ -136,28 +174,23 @@ components: AmountResponse: type: object description: > - Monetary amount returned by the API. Carries both an integer (for arithmetic) and a - decimal string (for display) so callers don't need to know per-asset precision upfront. - Request bodies use the scalar `AmountValue` instead. + Monetary amount expressed as a decimal string in the asset's canonical scale. Use a + decimal-precision library (`BigDecimal`, `Decimal`) for arithmetic — never JavaScript + `Number`. Request bodies use the scalar `AmountValue` instead. properties: value: - type: integer - description: Amount in minor units of `asset` (e.g., 50000 = 500.00 BRL). - example: 50000 - decimalValue: type: string pattern: '^-?\d+(\.\d+)?$' - description: Decimal representation in the asset's canonical scale. + description: Decimal amount in the asset's canonical scale. example: "500.00" asset: $ref: "#/components/schemas/Asset" decimals: type: integer - description: Number of fractional digits used to render `value` as `decimalValue`. + description: Number of decimal places for the asset. example: 2 required: - value - - decimalValue - asset - decimals Rate: @@ -223,17 +256,14 @@ components: status: $ref: "#/components/schemas/OperationStatus" reason: - oneOf: - - $ref: "#/components/schemas/Reason" - - type: "null" - description: Justification for entering this status. Required for `FAILED`, `ON_HOLD`, and `ACTION_REQUIRED`; `null` otherwise. + $ref: "#/components/schemas/Reason" + description: Justification for entering this status. Present for `FAILED`, `ON_HOLD`, and `ACTION_REQUIRED`; omitted otherwise. createdAt: type: string format: date-time description: Time the operation entered this status. required: - status - - reason - createdAt InstructionStatus: type: string @@ -246,22 +276,19 @@ components: `PENDING_REVIEW` and transition asynchronously to `APPROVED` or `REJECTED`. InstructionState: type: object - description: A single entry in a payment instruction's state history. Carries a status, optional reason, and the time the state was entered. + description: A single entry in a payment instruction's state history. Carries a status, optional reason (present only for `REJECTED`), and the time the state was entered. properties: status: $ref: "#/components/schemas/InstructionStatus" reason: - oneOf: - - $ref: "#/components/schemas/Reason" - - type: "null" - description: Justification for entering this status (e.g., why an instruction was `REJECTED`). `null` for non-terminal or non-rejected states. + $ref: "#/components/schemas/Reason" + description: Justification for entering this status (e.g., why an instruction was `REJECTED`). Present for `REJECTED`; omitted for `PENDING_REVIEW` and `APPROVED`. createdAt: type: string format: date-time description: Time the instruction entered this status. required: - status - - reason - createdAt Rail: type: string @@ -284,26 +311,45 @@ components: - INDIVIDUAL - COMPANY description: Whether a beneficiary or account holder is a person (CPF) or a company (CNPJ). - TaxId: + TaxIdRequest: type: object description: > - Tax identifier as a typed value object. The `number` must match the format expected + Tax identifier for a beneficiary holder. The `value` must match the format expected for the supplied `type` (e.g. a `CPF` must be 11 digits with valid check digits, a `CNPJ` must be 14 digits with valid check digits). properties: + value: + type: string + description: Tax identifier digits only (no dots, dashes, or slashes). + example: "52998224725" type: type: string description: > Tax identifier kind. Brazilian beneficiaries use `CPF` (individual) or `CNPJ` (company); other jurisdictions use the local code (e.g. `SSN`, `EIN`, `CUIT`). example: "CPF" - number: + country: + $ref: "#/components/schemas/Country" + required: + - value + - type + - country + TaxIdResponse: + type: object + description: Tax identifier returned for a beneficiary holder. + properties: + value: type: string - description: Tax identifier number, digits only (no dots, dashes, or slashes). example: "52998224725" + type: + type: string + example: "CPF" + country: + $ref: "#/components/schemas/Country" required: + - value - type - - number + - country Country: type: string description: ISO 3166-1 alpha-2 country code. @@ -569,24 +615,23 @@ components: description: PIX key category. EVP is a random key issued by the central bank. PageMetadata: type: object + description: Cursor-based pagination metadata. properties: previousCursor: type: string nullable: true - description: Cursor for the previous page. + description: Cursor for the previous page. `null` on the first page. nextCursor: type: string nullable: true - description: Cursor for the next page. + description: Cursor for the next page. `null` on the last page. total: type: integer - description: Total number of records. - totalMatches: - type: integer - description: Total records matching the current filter. + description: Number of items in the current page. required: + - previousCursor + - nextCursor - total - - totalMatches ErrorResponse: type: object properties: @@ -640,6 +685,7 @@ components: id: type: string format: uuid + readOnly: true customerId: type: string format: uuid @@ -743,12 +789,12 @@ components: operation. example: "f6a7b8c9-d0e1-2345-fabc-456789012cde" fundingInstruction: - $ref: "#/components/schemas/DepositFundingInstructionInput" + $ref: "#/components/schemas/DepositFundingInstructionRequest" required: - accountId - quoteId - fundingInstruction - DepositFundingInstructionInput: + DepositFundingInstructionRequest: type: object description: Selects which funding rail the customer will use to send the money in. properties: @@ -818,7 +864,7 @@ components: tradeName: type: string taxId: - $ref: "#/components/schemas/TaxId" + $ref: "#/components/schemas/TaxIdRequest" dateOfBirth: type: string format: date @@ -885,6 +931,7 @@ components: id: type: string format: uuid + readOnly: true customerId: type: string format: uuid @@ -916,9 +963,11 @@ components: createdAt: type: string format: date-time + readOnly: true updatedAt: type: string format: date-time + readOnly: true required: - id - customerId @@ -955,8 +1004,8 @@ components: - FX_SPREAD - PLATFORM - RAIL - - PARTNER - description: Where the fee originates. + - CUSTOMER + description: Where the fee originates. `PLATFORM` is charged by Trace, `RAIL` by the underlying banking rail (PIX, TED, wire), `CUSTOMER` is a passthrough markup added by the customer. createdAt: type: string format: date-time @@ -1199,7 +1248,7 @@ components: lastName: type: string taxId: - $ref: "#/components/schemas/TaxId" + $ref: "#/components/schemas/TaxIdResponse" dateOfBirth: type: string format: date @@ -1229,7 +1278,7 @@ components: - type: "null" description: Trade name (DBA). `null` if the company doesn't use one. taxId: - $ref: "#/components/schemas/TaxId" + $ref: "#/components/schemas/TaxIdResponse" address: $ref: "#/components/schemas/Address" required: @@ -1478,7 +1527,7 @@ components: description: Family name. example: "Silva" taxId: - $ref: "#/components/schemas/TaxId" + $ref: "#/components/schemas/TaxIdRequest" dateOfBirth: type: string format: date @@ -1511,7 +1560,7 @@ components: description: Trade name. Optional. example: "Acme" taxId: - $ref: "#/components/schemas/TaxId" + $ref: "#/components/schemas/TaxIdRequest" address: $ref: "#/components/schemas/Address" required: @@ -1627,6 +1676,7 @@ components: type: string format: uuid example: "ben-a1b2c3d4-5e6f-7890-abcd-ef1234567890" + readOnly: true customerId: type: string format: uuid @@ -1647,10 +1697,12 @@ components: type: string format: date-time example: "2026-04-14T10:30:00Z" + readOnly: true updatedAt: type: string format: date-time example: "2026-04-14T10:30:00Z" + readOnly: true required: - id - customerId @@ -1667,6 +1719,7 @@ components: type: string format: uuid example: "ben-a1b2c3d4-5e6f-7890-abcd-ef1234567890" + readOnly: true customerId: type: string format: uuid @@ -1687,10 +1740,12 @@ components: type: string format: date-time example: "2026-04-14T10:30:00Z" + readOnly: true updatedAt: type: string format: date-time example: "2026-04-14T10:30:00Z" + readOnly: true required: - id - customerId @@ -1724,7 +1779,7 @@ components: type: string example: "Silva" taxId: - $ref: "#/components/schemas/TaxId" + $ref: "#/components/schemas/TaxIdResponse" dateOfBirth: type: string format: date @@ -1755,7 +1810,7 @@ components: nullable: true example: "Acme" taxId: - $ref: "#/components/schemas/TaxId" + $ref: "#/components/schemas/TaxIdResponse" address: $ref: "#/components/schemas/Address" required: @@ -1905,12 +1960,13 @@ components: - address PaymentInstructionResponse: type: object - description: A payment instruction on a beneficiary. Use `address.rail` to discriminate routing details — for beneficiary instructions it is one of `PIX_KEY`, `PIX_BANK_INSTRUCTION`, or `CRYPTO`. + description: A payment instruction on a beneficiary. Use `address.rail` to discriminate routing details — for beneficiary payment instructions it is one of `PIX_KEY`, `PIX_BANK_INSTRUCTION`, or `CRYPTO`. properties: id: type: string format: uuid example: "pi-a1b2c3d4-5e6f-7890-abcd-ef1234567890" + readOnly: true asset: type: string description: Asset/currency for this instruction. @@ -1923,6 +1979,7 @@ components: type: string format: date-time example: "2026-04-14T10:30:00Z" + readOnly: true required: - id - asset @@ -1932,6 +1989,9 @@ components: responses: UnauthorizedError: description: Missing or invalid authentication token. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2073,6 +2133,9 @@ paths: responses: "201": description: Withdrawal accepted. The operation is in `REQUESTED` status. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2087,24 +2150,20 @@ paths: id: "a1b2c3d4-5e6f-7890-abcd-ef1234567890" owner: "Acme Importação Ltda" sourceAmount: - value: 50000 - decimalValue: "500.00" + value: "500.00" asset: "BRL" decimals: 2 targetAmount: - value: 50000 - decimalValue: "500.00" + value: "500.00" asset: "BRL" decimals: 2 fees: [] transactions: [] currentState: status: "REQUESTED" - reason: null createdAt: "2026-04-25T02:39:17Z" states: - status: "REQUESTED" - reason: null createdAt: "2026-04-25T02:39:17Z" intent: type: "WITHDRAWAL" @@ -2115,8 +2174,9 @@ paths: firstName: "John" lastName: "Doe" taxId: + value: "52998224725" type: "CPF" - number: "52998224725" + country: "BR" dateOfBirth: "1990-01-15" address: addressLine1: "Rua Augusta, 500" @@ -2142,6 +2202,9 @@ paths: updatedAt: "2026-04-25T02:39:17Z" "400": description: > + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" Validation error: missing or malformed fields, inline beneficiary not supported, or inline instruction not supported. content: @@ -2199,6 +2262,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Account, beneficiary, or payment instruction not found for this customer. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2214,6 +2280,9 @@ paths: $ref: "#/components/examples/QuoteNotFound" "409": description: Idempotency key was reused with a different request body. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2223,6 +2292,9 @@ paths: $ref: "#/components/examples/IdempotencyConflict" "422": description: Business rule violation. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2286,6 +2358,9 @@ paths: responses: "201": description: Swap accepted. The operation is in `REQUESTED` status. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2300,24 +2375,20 @@ paths: id: "a1b2c3d4-5e6f-7890-abcd-ef1234567890" owner: "Acme Importação Ltda" sourceAmount: - value: 500000 - decimalValue: "5000.00" + value: "5000.00" asset: "BRL" decimals: 2 targetAmount: - value: 100000 - decimalValue: "1000.00" + value: "1000.00" asset: "USD" decimals: 2 fees: [] transactions: [] currentState: status: "REQUESTED" - reason: null createdAt: "2026-04-27T14:12:33Z" states: - status: "REQUESTED" - reason: null createdAt: "2026-04-27T14:12:33Z" intent: type: "SWAP" @@ -2333,6 +2404,9 @@ paths: updatedAt: "2026-04-27T14:12:33Z" "400": description: "Validation error: missing or malformed fields." + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2366,6 +2440,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Account or quote not found for this customer. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2377,6 +2454,9 @@ paths: $ref: "#/components/examples/QuoteNotFound" "409": description: Idempotency key was reused with a different request body. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2386,6 +2466,9 @@ paths: $ref: "#/components/examples/IdempotencyConflict" "422": description: Business rule violation. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2437,6 +2520,9 @@ paths: responses: "201": description: Deposit accepted. The operation is in `REQUESTED` status. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2451,24 +2537,20 @@ paths: id: "a1b2c3d4-5e6f-7890-abcd-ef1234567890" owner: "Acme Importação Ltda" sourceAmount: - value: 50000 - decimalValue: "500.00" + value: "500.00" asset: "BRL" decimals: 2 targetAmount: - value: 50000 - decimalValue: "500.00" + value: "500.00" asset: "BRL" decimals: 2 fees: [] transactions: [] currentState: status: "REQUESTED" - reason: null createdAt: "2026-04-27T15:08:21Z" states: - status: "REQUESTED" - reason: null createdAt: "2026-04-27T15:08:21Z" intent: type: "DEPOSIT" @@ -2488,6 +2570,9 @@ paths: updatedAt: "2026-04-27T15:08:21Z" "400": description: "Validation error: missing or malformed fields, or unsupported rail." + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2521,6 +2606,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Account or quote not found for this customer. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2532,6 +2620,9 @@ paths: $ref: "#/components/examples/QuoteNotFound" "409": description: Idempotency key was reused with a different request body. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2541,6 +2632,9 @@ paths: $ref: "#/components/examples/IdempotencyConflict" "422": description: Business rule violation. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2611,6 +2705,9 @@ paths: responses: "201": description: Quote created. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2625,13 +2722,11 @@ paths: id: "a1b2c3d4-5e6f-7890-abcd-ef1234567890" owner: "Acme Importação Ltda" sourceAmount: - value: 50000 - decimalValue: "500.00" + value: "500.00" asset: "BRL" decimals: 2 targetAmount: - value: 100000000 - decimalValue: "100.000000" + value: "100.000000" asset: "USDT" decimals: 6 effectiveRate: @@ -2648,13 +2743,11 @@ paths: id: "a1b2c3d4-5e6f-7890-abcd-ef1234567890" owner: "Acme Importação Ltda" sourceAmount: - value: 50000 - decimalValue: "500.00" + value: "500.00" asset: "BRL" decimals: 2 targetAmount: - value: 50000 - decimalValue: "500.00" + value: "500.00" asset: "BRL" decimals: 2 effectiveRate: @@ -2664,6 +2757,9 @@ paths: expiresAt: "2026-04-25T02:39:47Z" "400": description: Validation error. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2731,6 +2827,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "422": description: Quote could not be created (no rate available, asset pair unsupported, etc.). + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2760,6 +2859,9 @@ paths: responses: "200": description: Operation details. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2768,6 +2870,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Operation not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2783,31 +2888,22 @@ paths: - Operations summary: List operations description: > - Lists all operations for the authenticated customer. Results are paginated and can - be filtered by status or by intent type. + Lists all operations for the authenticated customer. Use the `filters` query parameter + to narrow by status, intent type, asset, or other fields — see the + [Filtering](/guides/principles/filtering) guide. parameters: - $ref: "#/components/parameters/TraceVersion" - $ref: "#/components/parameters/PaginationLimit" - $ref: "#/components/parameters/PaginationCursor" - - name: intentType - in: query - required: false - description: Filter by operation intent type. - schema: - type: string - enum: - - WITHDRAWAL - - SWAP - - DEPOSIT - - name: status - in: query - required: false - description: Filter by operation status. - schema: - $ref: "#/components/schemas/OperationStatus" + - $ref: "#/components/parameters/PaginationDirection" + - $ref: "#/components/parameters/PaginationSortOrder" + - $ref: "#/components/parameters/Filters" responses: "200": description: Paginated list of operations. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2826,6 +2922,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "422": description: Invalid filter parameter. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2880,8 +2979,9 @@ paths: firstName: "João" lastName: "Silva" taxId: + value: "52998224725" type: "CPF" - number: "52998224725" + country: "BR" dateOfBirth: "1985-03-15" address: addressLine1: "Av. Paulista, 1000" @@ -2904,8 +3004,9 @@ paths: legalName: "Acme Importação Ltda" tradeName: "Acme" taxId: + value: "27922482000193" type: "CNPJ" - number: "27922482000193" + country: "BR" address: addressLine1: "Rua da Consolação, 222" city: "São Paulo" @@ -2927,8 +3028,9 @@ paths: firstName: "Jane" lastName: "Doe" taxId: + value: "11144477735" type: "CPF" - number: "11144477735" + country: "BR" dateOfBirth: "1990-07-22" address: addressLine1: "123 Main St" @@ -2944,6 +3046,9 @@ paths: responses: "201": description: Beneficiary created. Its initial payment instruction starts in `PENDING_REVIEW` status. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2952,6 +3057,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "422": description: Validation error (invalid holder type, invalid instruction type, missing details). + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -2988,9 +3096,15 @@ paths: - $ref: "#/components/parameters/TraceVersion" - $ref: "#/components/parameters/PaginationLimit" - $ref: "#/components/parameters/PaginationCursor" + - $ref: "#/components/parameters/PaginationDirection" + - $ref: "#/components/parameters/PaginationSortOrder" + - $ref: "#/components/parameters/Filters" responses: "200": description: Paginated list of beneficiary summaries. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -3021,6 +3135,9 @@ paths: responses: "200": description: Beneficiary details. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -3029,6 +3146,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Beneficiary not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -3048,10 +3168,16 @@ paths: responses: "204": description: Beneficiary deleted. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" "401": $ref: "#/components/responses/UnauthorizedError" "404": description: Beneficiary not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -3097,6 +3223,9 @@ paths: responses: "201": description: Payment instruction added. Returns the full beneficiary. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -3105,6 +3234,9 @@ paths: $ref: "#/components/responses/UnauthorizedError" "404": description: Beneficiary not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -3114,6 +3246,9 @@ paths: $ref: "#/components/examples/BeneficiaryNotFound" "422": description: Validation error (invalid instruction type, missing details). + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -3145,10 +3280,16 @@ paths: responses: "204": description: Payment instruction removed. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" "401": $ref: "#/components/responses/UnauthorizedError" "404": description: Beneficiary or payment instruction not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: diff --git a/apis/fx-webhook/openapi.yml b/apis/fx-webhook/openapi.yml index 2b7844a..9a364fa 100644 --- a/apis/fx-webhook/openapi.yml +++ b/apis/fx-webhook/openapi.yml @@ -25,7 +25,13 @@ components: type: http scheme: bearer bearerFormat: JWT - description: "Auth0 JWT token. Include as `Authorization: Bearer `." + description: "JWT bearer token. Include as `Authorization: Bearer `. See the [Authentication](/guides/authentication) guide for how to obtain one." + headers: + RequestId: + description: Unique request identifier emitted on every response. Reference it when contacting Trace support so we can trace the request end-to-end. See [Errors](/guides/principles/errors). + schema: + type: string + format: uuid parameters: SubscriptionId: name: subscriptionId @@ -54,8 +60,8 @@ components: IdempotencyKey: name: X-Idempotency-Key in: header - required: false - description: Unique key to ensure idempotent request processing. + required: true + description: Unique key to ensure idempotent request processing. Required on all `POST`, `PUT`, and `PATCH` requests. schema: type: string format: uuid @@ -97,9 +103,32 @@ components: - ASCENDING - DESCENDING default: DESCENDING + Filters: + name: filters + in: query + required: false + description: | + Filter expression using LHS Brackets syntax (`field[operator]=value`). + Combine multiple conditions with `and(...)`, `or(...)`, or `;` (implicit AND). + See the [Filtering](/guides/principles/filtering) guide for the full operator catalog and examples. + schema: + type: string + example: "status[eq]=ACTIVE" responses: WebhookAck: description: Acknowledged. Trace considers any 2xx response a successful delivery. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" + UnauthorizedError: + description: Missing or invalid authentication token. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorResponse" schemas: ResourceName: type: string @@ -113,12 +142,12 @@ components: type: string description: The specific event type within a resource, sent as the `X-Event-Type` header on each delivery. enum: - - ASSET_ACTIVATED - - BENEFICIARY_INSTRUCTION_CREATED - - BENEFICIARY_INSTRUCTION_APPROVED - - BENEFICIARY_INSTRUCTION_REJECTED - - OPERATION_REQUESTED - example: OPERATION_REQUESTED + - account.asset.activated + - beneficiary.paymentInstruction.created + - beneficiary.paymentInstruction.approved + - beneficiary.paymentInstruction.rejected + - operation.requested + example: operation.requested ExecutionLogStatus: type: string enum: @@ -126,15 +155,28 @@ components: - FAILED description: Outcome of a delivery attempt. `SUCCESS` indicates a 2xx response was received; `FAILED` indicates a non-2xx response or a network error. example: SUCCESS - Currency: + Asset: type: string - description: ISO 4217 currency code or stablecoin symbol. + description: ISO 4217 currency code or stablecoin ticker. enum: - - BRL - USD + - BRL - EUR - - USDT + - CAD + - AUD + - GBP + - MXN + - HKD + - SGD + - PHP + - NZD + - CHF + - NOK + - DKK + - PLN + - SEK - USDC + - USDT - EURC - BRLT example: BRL @@ -154,8 +196,8 @@ components: items: $ref: "#/components/schemas/EventType" example: - - BENEFICIARY_INSTRUCTION_APPROVED - - BENEFICIARY_INSTRUCTION_REJECTED + - beneficiary.paymentInstruction.approved + - beneficiary.paymentInstruction.rejected required: - name SubscriptionResource: @@ -218,12 +260,13 @@ components: type: boolean Subscription: type: object - description: A webhook subscription that delivers events from one or more resources to a partner-controlled URL. + description: A webhook subscription that delivers events from one or more resources to a customer-controlled URL. properties: id: type: string format: uuid example: "8a13c6a4-1a3d-4f49-ad62-9dca8c47e2c6" + readOnly: true companyId: type: string description: Company identifier that owns the subscription. @@ -245,10 +288,12 @@ components: type: string format: date-time example: "2026-04-28T14:32:11Z" + readOnly: true updatedAt: type: string format: date-time example: "2026-04-28T14:32:11Z" + readOnly: true required: - id - companyId @@ -265,7 +310,7 @@ components: items: $ref: "#/components/schemas/Subscription" meta: - $ref: "#/components/schemas/PaginationMeta" + $ref: "#/components/schemas/PageMetadata" required: - data - meta @@ -278,6 +323,7 @@ components: type: string format: uuid example: "7c9b46e8-3f33-4edc-94e2-bd4b58e0c62f" + readOnly: true companyId: type: string description: Company identifier that owns the subscription. @@ -303,7 +349,7 @@ components: example: "{\"id\":\"1f3a8c8d-2e1a-4b3a-9d2e-7c1a4b3a9d2e\",\"customerId\":\"...\",\"atTime\":\"2026-04-28T14:32:11Z\"}" httpStatus: type: integer - description: HTTP status code returned by the partner endpoint. `0` if the request never received a response. + description: HTTP status code returned by the customer endpoint. `0` if the request never received a response. example: 200 error: type: string @@ -312,6 +358,7 @@ components: example: null status: $ref: "#/components/schemas/ExecutionLogStatus" + readOnly: true retryAttempts: type: integer description: Number of retry attempts made after the initial delivery. @@ -320,10 +367,12 @@ components: type: string format: date-time example: "2026-04-28T14:32:11Z" + readOnly: true updatedAt: type: string format: date-time example: "2026-04-28T14:32:12Z" + readOnly: true required: - id - companyId @@ -345,7 +394,7 @@ components: items: $ref: "#/components/schemas/ExecutionLog" meta: - $ref: "#/components/schemas/PaginationMeta" + $ref: "#/components/schemas/PageMetadata" required: - data - meta @@ -364,7 +413,7 @@ components: - resourceName - eventTypes - PaginationMeta: + PageMetadata: type: object description: Cursor-based pagination metadata. properties: @@ -406,15 +455,12 @@ components: AmountEvent: type: object - description: Monetary amount carrying both an integer (for arithmetic) and a decimal string (for display). + description: Monetary amount expressed as a decimal string in the asset's canonical scale. Use a decimal-precision library for arithmetic — never JavaScript `Number`. properties: value: type: string - description: Value in minor units, serialized as a decimal string to preserve precision. - example: "50000" - decimalValue: - type: string - description: Same value formatted with the asset's decimal places, ready to display. + pattern: '^-?\d+(\.\d+)?$' + description: Decimal amount in the asset's canonical scale. example: "500.00" asset: type: string @@ -426,7 +472,6 @@ components: example: 2 required: - value - - decimalValue - asset - decimals AddressEvent: @@ -467,6 +512,7 @@ components: type: string format: uuid example: "a1b2c3d4-5e6f-7890-abcd-ef1234567890" + readOnly: true owner: type: string description: Registered account owner name. @@ -483,9 +529,9 @@ components: description: Decimal rate, source-asset per target-asset. example: "1.00" base: - $ref: "#/components/schemas/Currency" + $ref: "#/components/schemas/Asset" quote: - $ref: "#/components/schemas/Currency" + $ref: "#/components/schemas/Asset" required: - value - base @@ -498,6 +544,7 @@ components: type: string format: uuid example: "d4e5f6a7-b8c9-0123-defa-2345678901bc" + readOnly: true spotRate: $ref: "#/components/schemas/SpotRateEvent" required: @@ -540,14 +587,14 @@ components: - FX_SPREAD - PLATFORM - RAIL - - PARTNER - description: Origin of the fee charged. + - CUSTOMER + description: Origin of the fee charged. `PLATFORM` is charged by Trace, `RAIL` by the underlying banking rail (PIX, TED, wire), `CUSTOMER` is a passthrough markup added by the customer. Direction: type: string enum: - CREDIT - DEBIT - description: Direction a transaction moves funds, relative to the partner's account. + description: Direction a transaction moves funds, relative to the customer's account. TransactionStatus: type: string enum: @@ -780,10 +827,12 @@ components: type: string format: uuid example: "c3d4e5f6-a7b8-9012-cdef-1234567890ab" + readOnly: true asset: - $ref: "#/components/schemas/Currency" + $ref: "#/components/schemas/Asset" status: $ref: "#/components/schemas/PaymentInstructionEventStatus" + readOnly: true address: $ref: "#/components/schemas/FinancialAddressEvent" required: @@ -815,6 +864,7 @@ components: type: string format: uuid example: "c3d4e5f6-a7b8-9012-cdef-1234567890ab" + readOnly: true address: $ref: "#/components/schemas/FinancialAddressEvent" required: @@ -830,6 +880,7 @@ components: nullable: true description: ID of the saved beneficiary, when one exists. example: "b2c3d4e5-f6a7-8901-bcde-f12345678901" + readOnly: true holder: $ref: "#/components/schemas/HolderReferenceEvent" instruction: @@ -924,6 +975,7 @@ components: id: type: string description: Rail-side transaction identifier. + readOnly: true direction: $ref: "#/components/schemas/Direction" amount: @@ -954,10 +1006,11 @@ components: type: array description: Assets activated by this event. items: - $ref: "#/components/schemas/Currency" + $ref: "#/components/schemas/Asset" atTime: type: string format: date-time + description: When this event occurred. Distinct from the resource's `createdAt` — events fire on state transitions, so `atTime` reflects when the transition happened, not when the underlying resource was first created. required: - accountId - customerId @@ -966,9 +1019,9 @@ components: BeneficiaryEvent: type: object description: > - Payload delivered for beneficiary instruction lifecycle events - (`BENEFICIARY_INSTRUCTION_CREATED`, `BENEFICIARY_INSTRUCTION_APPROVED`, - `BENEFICIARY_INSTRUCTION_REJECTED`). + Payload delivered for beneficiary payment-instruction lifecycle events + (`beneficiary.paymentInstruction.created`, `beneficiary.paymentInstruction.approved`, + `beneficiary.paymentInstruction.rejected`). Review status is tracked **per payment instruction**, not on the beneficiary itself. The payload always carries the full beneficiary @@ -979,6 +1032,7 @@ components: id: type: string format: uuid + readOnly: true customerId: type: string format: uuid @@ -992,6 +1046,7 @@ components: atTime: type: string format: date-time + description: When this event occurred. Distinct from the resource's `createdAt` — events fire on state transitions, so `atTime` reflects when the transition happened, not when the underlying resource was first created. required: - id - customerId @@ -1001,13 +1056,16 @@ components: OperationEvent: type: object description: > - Payload delivered for operation lifecycle events - (`OPERATION_REQUESTED` and additional events as the operation - progresses). + Payload delivered when an operation is first created + (`operation.requested`). Subsequent state transitions + (`PROCESSING`, `COMPLETED`, `FAILED`, etc.) are not published as + webhook events — poll `GET /api/operations/{operationId}` to + track lifecycle progress. properties: id: type: string format: uuid + readOnly: true customerId: type: string account: @@ -1035,6 +1093,7 @@ components: atTime: type: string format: date-time + description: When this event occurred. Distinct from the resource's `createdAt` — events fire on state transitions, so `atTime` reflects when the transition happened, not when the underlying resource was first created. required: - id - customerId @@ -1085,22 +1144,30 @@ paths: resources: - name: BENEFICIARY events: - - BENEFICIARY_INSTRUCTION_APPROVED - - BENEFICIARY_INSTRUCTION_REJECTED + - beneficiary.paymentInstruction.approved + - beneficiary.paymentInstruction.rejected allowRetry: true responses: "201": description: Subscription created. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/Subscription" "400": description: Validation error. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" + "401": + $ref: "#/components/responses/UnauthorizedError" get: operationId: listSubscriptions tags: @@ -1113,13 +1180,19 @@ paths: - $ref: "#/components/parameters/PaginationCursor" - $ref: "#/components/parameters/PaginationDirection" - $ref: "#/components/parameters/PaginationSortOrder" + - $ref: "#/components/parameters/Filters" responses: "200": description: Paginated list of subscriptions. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/SubscriptionList" + "401": + $ref: "#/components/responses/UnauthorizedError" /api/subscriptions/{subscriptionId}: get: operationId: getSubscription @@ -1132,16 +1205,24 @@ paths: responses: "200": description: Subscription details. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/Subscription" "404": description: Subscription not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" + "401": + $ref: "#/components/responses/UnauthorizedError" patch: operationId: updateSubscription tags: @@ -1171,7 +1252,7 @@ paths: includeAll: true - name: BENEFICIARY events: - - BENEFICIARY_INSTRUCTION_APPROVED + - beneficiary.paymentInstruction.approved disable-retry: summary: Stop retrying failed deliveries value: @@ -1179,16 +1260,24 @@ paths: responses: "200": description: Updated subscription. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/Subscription" "404": description: Subscription not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" + "401": + $ref: "#/components/responses/UnauthorizedError" delete: operationId: deleteSubscription tags: @@ -1201,13 +1290,21 @@ paths: responses: "204": description: Subscription deleted. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" "404": description: Subscription not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" + "401": + $ref: "#/components/responses/UnauthorizedError" /api/subscriptions/{subscriptionId}/executionLogs: get: operationId: listExecutionLogs @@ -1222,26 +1319,35 @@ paths: - $ref: "#/components/parameters/PaginationCursor" - $ref: "#/components/parameters/PaginationDirection" - $ref: "#/components/parameters/PaginationSortOrder" + - $ref: "#/components/parameters/Filters" responses: "200": description: Paginated list of execution logs. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ExecutionLogList" "404": description: Subscription not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" + "401": + $ref: "#/components/responses/UnauthorizedError" /api/subscriptions/{subscriptionId}/executionLogs/{executionLogId}: get: operationId: getExecutionLog tags: - Execution logs summary: Get an execution log - description: Retrieve a single delivery attempt, including the request payload and partner response status. + description: Retrieve a single delivery attempt, including the request payload and customer response status. parameters: - $ref: "#/components/parameters/SubscriptionId" - $ref: "#/components/parameters/ExecutionLogId" @@ -1249,16 +1355,24 @@ paths: responses: "200": description: Execution log entry. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ExecutionLog" "404": description: Execution log not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" + "401": + $ref: "#/components/responses/UnauthorizedError" /api/subscriptions/{subscriptionId}/executionLogs/{executionLogId}/resend: post: operationId: resendExecutionLog @@ -1267,7 +1381,7 @@ paths: summary: Resend a delivery description: > Re-send a previously failed (or successful) delivery using the same - payload, headers, and signature. Useful when a partner endpoint was + payload, headers, and signature. Useful when a customer endpoint was unavailable during the original window. parameters: - $ref: "#/components/parameters/SubscriptionId" @@ -1277,13 +1391,21 @@ paths: responses: "204": description: Resend accepted. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" "404": description: Execution log not found. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: $ref: "#/components/schemas/ErrorResponse" + "401": + $ref: "#/components/responses/UnauthorizedError" /references/ResourceName/all: get: operationId: listResourceReferences @@ -1300,6 +1422,9 @@ paths: responses: "200": description: Resource catalog. + headers: + X-Request-Id: + $ref: "#/components/headers/RequestId" content: application/json: schema: @@ -1323,7 +1448,9 @@ webhooks: responses: "200": $ref: "#/components/responses/WebhookAck" - beneficiary.instruction.created: + "401": + $ref: "#/components/responses/UnauthorizedError" + beneficiary.paymentInstruction.created: post: summary: Beneficiary instruction created description: > @@ -1344,7 +1471,9 @@ webhooks: responses: "200": $ref: "#/components/responses/WebhookAck" - beneficiary.instruction.approved: + "401": + $ref: "#/components/responses/UnauthorizedError" + beneficiary.paymentInstruction.approved: post: summary: Beneficiary instruction approved description: > @@ -1364,7 +1493,9 @@ webhooks: responses: "200": $ref: "#/components/responses/WebhookAck" - beneficiary.instruction.rejected: + "401": + $ref: "#/components/responses/UnauthorizedError" + beneficiary.paymentInstruction.rejected: post: summary: Beneficiary instruction rejected description: > @@ -1385,6 +1516,8 @@ webhooks: "200": $ref: "#/components/responses/WebhookAck" + "401": + $ref: "#/components/responses/UnauthorizedError" operation.requested: post: summary: Operation requested @@ -1400,3 +1533,6 @@ webhooks: responses: "200": $ref: "#/components/responses/WebhookAck" + + "401": + $ref: "#/components/responses/UnauthorizedError" \ No newline at end of file diff --git a/docs.json b/docs.json index d56eded..83c18d4 100644 --- a/docs.json +++ b/docs.json @@ -217,9 +217,9 @@ "group": "Beneficiary", "icon": "address-book", "pages": [ - "api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-created", - "api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-approved", - "api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-rejected" + "api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created", + "api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-approved", + "api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected" ] } ] diff --git a/guides/environments.mdx b/guides/environments.mdx index cd53901..bffe9f4 100644 --- a/guides/environments.mdx +++ b/guides/environments.mdx @@ -24,7 +24,7 @@ The sandbox is an isolated environment that replicates production behavior with ### Production -The production environment processes real transactions and serves live users. Access is granted after your integration has been validated in sandbox. +The production environment processes real transactions and serves live account owners. Access is granted after your integration has been validated in sandbox. - All operations affect real accounts and funds. - Subject to full compliance and security requirements. diff --git a/guides/principles/datetime.mdx b/guides/principles/datetime.mdx index 5c82db9..7094883 100644 --- a/guides/principles/datetime.mdx +++ b/guides/principles/datetime.mdx @@ -15,7 +15,7 @@ All dates and timestamps in the Trace FX API use **UTC** in ISO 8601 format. No yyyy-MM-ddTHH:mm:ss.SSSZ ``` -Example: `2025-01-15T14:30:00.000Z` +Example: `2026-04-22T20:00:00Z` | Component | Description | Example | | --- | --- | --- | @@ -35,12 +35,12 @@ A resource creation timestamp: ```json { - "createdAt": "2025-01-15T14:30:00.000Z" + "createdAt": "2026-04-22T20:00:00Z" } ``` Filtering by date range: ```bash -?filters=and(createdAt[gte]=2025-01-01T00:00:00.000Z,createdAt[lte]=2025-01-31T23:59:59.999Z) +?filters=and(createdAt[gte]=2026-04-01T00:00:00Z,createdAt[lte]=2026-04-30T23:59:59Z) ``` diff --git a/guides/principles/money.mdx b/guides/principles/money.mdx index e63bbf7..9cced3f 100644 --- a/guides/principles/money.mdx +++ b/guides/principles/money.mdx @@ -5,18 +5,21 @@ description: "How monetary amounts are represented in the API." ## Overview -All monetary amounts in the Trace FX API are represented as integers in **minor units** (e.g. cents) paired with an ISO 4217 currency code. This avoids floating-point precision issues. +Monetary amounts in the Trace FX API are represented as **decimal strings** in the asset's canonical scale, paired with an ISO 4217 currency code or stablecoin ticker. This single shape works uniformly for fiat (2 decimals), stablecoins (6 decimals), and high-precision crypto (8+ decimals). + + + Always parse amounts with a decimal-precision library (`BigDecimal`, `decimal.Decimal`, `Decimal.js`). Never use JavaScript `Number` or any 64-bit float — values like `0.1 + 0.2` lose precision, and high-decimal tokens overflow the safe-integer range. + ## How it works ### Amount object -Every amount in the API uses the same structure: +Every amount in a response uses the same structure: ```json { - "value": 11, - "decimalValue": "0.11", + "value": "0.11", "asset": "BRL", "decimals": 2 } @@ -24,22 +27,33 @@ Every amount in the API uses the same structure: | Field | Type | Description | | --- | --- | --- | -| `value` | integer | Amount in the smallest currency unit (e.g. centavos for BRL, cents for USD) | -| `decimalValue` | string | Human-readable decimal representation of the amount | -| `asset` | string | ISO 4217 currency code | -| `decimals` | integer | Number of decimal places for the currency | +| `value` | string | Decimal amount in the asset's canonical scale. Always a string to preserve precision. | +| `asset` | string | ISO 4217 currency code or stablecoin ticker. | +| `decimals` | integer | Number of decimal places for the asset. | -### Examples by currency +### In request bodies -| `value` | `decimals` | `asset` | `decimalValue` | -| --- | --- | --- | --- | -| `500000` | `2` | `BRL` | `"5000.00"` | -| `1050` | `2` | `USD` | `"10.50"` | -| `100000000` | `8` | `BTC` | `"1.00000000"` | +Quote and operation requests take amounts as a **decimal-string scalar** paired with a separate asset field — not the full object: - - Always use integer arithmetic when working with `value`. The `decimalValue` field is provided for display purposes only. - +```json +{ + "sourceAmount": "500.00", + "sourceAsset": "BRL" +} +``` + +The number of fractional digits must not exceed the asset's precision (table below). Exceeding it returns `INVALID_AMOUNT_PRECISION`. + +### Decimal precision per asset + +| `asset` | `decimals` | Example `value` | +| --- | --- | --- | +| `BRL` | 2 | `"5000.00"` | +| `USD` | 2 | `"10.50"` | +| `EUR` | 2 | `"10.50"` | +| `USDC` | 6 | `"1.500000"` | +| `USDT` | 6 | `"100.000000"` | +| `BTC` | 8 | `"1.00000000"` | ## Examples @@ -48,8 +62,7 @@ A deposit of R$ 1.250,00: ```json { "amount": { - "value": 125000, - "decimalValue": "1250.00", + "value": "1250.00", "asset": "BRL", "decimals": 2 } @@ -61,8 +74,7 @@ A withdrawal of $500.00: ```json { "amount": { - "value": 50000, - "decimalValue": "500.00", + "value": "500.00", "asset": "USD", "decimals": 2 } diff --git a/index.mdx b/index.mdx index 5b6df61..cdb2c4f 100644 --- a/index.mdx +++ b/index.mdx @@ -23,7 +23,7 @@ mode: "wide" - Create accounts that hold fiat and crypto for your users. + Create accounts that hold fiat and crypto for your account owners. Accept deposits via PIX, wire transfer, and crypto networks. diff --git a/journeys/deposit.mdx b/journeys/deposit.mdx index c51d014..80cd9a7 100644 --- a/journeys/deposit.mdx +++ b/journeys/deposit.mdx @@ -61,7 +61,7 @@ Deposits credit an account when funds arrive via the chosen funding rail. The cu - Poll `GET /api/operations/{operationId}` or subscribe to webhook events. Once the inbound payment is reconciled, the operation transitions to `COMPLETED`. + Poll `GET /api/operations/{operationId}` until the inbound payment is reconciled and the operation transitions to `COMPLETED`. Lifecycle transitions are not published as webhook events; the `operation.requested` webhook only fires on creation. ```bash curl --request GET \ diff --git a/journeys/swap.mdx b/journeys/swap.mdx index 0231ac0..c68fc37 100644 --- a/journeys/swap.mdx +++ b/journeys/swap.mdx @@ -54,7 +54,7 @@ Swaps convert funds between assets within the same account — for example, BRL - Poll `GET /api/operations/{operationId}` or subscribe to webhook events to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). + Poll `GET /api/operations/{operationId}` to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). Lifecycle transitions are not published as webhook events; the `operation.requested` webhook only fires on creation. ```bash curl --request GET \ diff --git a/journeys/withdrawal.mdx b/journeys/withdrawal.mdx index 80491e6..22e1a90 100644 --- a/journeys/withdrawal.mdx +++ b/journeys/withdrawal.mdx @@ -29,13 +29,19 @@ Withdrawals move funds out of an account to an external destination — a bank a "type": "INDIVIDUAL", "firstName": "John", "lastName": "Doe", - "taxId": "12345678901", + "taxId": { + "value": "12345678901", + "type": "CPF", + "country": "BR" + }, "dateOfBirth": "1990-01-15" }, + "relationshipType": "THIRD_PARTY", "paymentInstruction": { "type": "PIX", - "pixKeyType": "CPF", - "pixKey": "12345678901" + "asset": "BRL", + "dictKeyType": "CPF", + "dictKey": "12345678901" } }' ``` @@ -90,7 +96,7 @@ Withdrawals move funds out of an account to an external destination — a bank a - Poll `GET /api/operations/{operationId}` or subscribe to webhook events to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). Compliance review can pause the operation in `ON_HOLD` or `ACTION_REQUIRED` until additional checks clear. + Poll `GET /api/operations/{operationId}` to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). Compliance review can pause the operation in `ON_HOLD` or `ACTION_REQUIRED` until additional checks clear. Lifecycle transitions are not published as webhook events; the `operation.requested` webhook only fires on creation. ```bash curl --request GET \ diff --git a/snippets/auth-header.mdx b/snippets/auth-header.mdx deleted file mode 100644 index 4e600ac..0000000 --- a/snippets/auth-header.mdx +++ /dev/null @@ -1,7 +0,0 @@ -All requests require a valid Auth0 JWT in the `Authorization` header: - -```bash -Authorization: Bearer -``` - -See [Authentication](/guides/authentication) for how to obtain a token. diff --git a/snippets/error-response.mdx b/snippets/error-response.mdx deleted file mode 100644 index 2e4d482..0000000 --- a/snippets/error-response.mdx +++ /dev/null @@ -1,11 +0,0 @@ -All errors follow the same shape: - -```json -{ - "code": "ERROR_CODE", - "message": "Human-readable description of what went wrong", - "details": {} -} -``` - -See [Errors](/guides/principles/errors) for error handling details. diff --git a/snippets/idempotency-note.mdx b/snippets/idempotency-note.mdx deleted file mode 100644 index 950bba4..0000000 --- a/snippets/idempotency-note.mdx +++ /dev/null @@ -1,5 +0,0 @@ - - This endpoint requires the `X-Idempotency-Key` header on every request. - Use a unique key (such as a UUID) per operation to safely retry on network failures. - See [Idempotency](/guides/principles/idempotency) for details. - diff --git a/snippets/money-format.mdx b/snippets/money-format.mdx index c46576a..58f7ef3 100644 --- a/snippets/money-format.mdx +++ b/snippets/money-format.mdx @@ -1,10 +1,11 @@ -Monetary amounts use **minor units** (integers) with an ISO 4217 currency code: +Monetary amounts are **decimal strings** in the asset's canonical scale, paired with an ISO 4217 currency code or stablecoin ticker: ```json { - "value": 500000, - "asset": "BRL" + "value": "5000.00", + "asset": "BRL", + "decimals": 2 } ``` -The example above represents **R$ 5.000,00** (5,000 BRL). See [Money and currencies](/guides/principles/money). +Always parse with a decimal-precision library (`BigDecimal`, `Decimal`) — never a 64-bit float. See [Money and currencies](/guides/principles/money). diff --git a/snippets/pagination-response.mdx b/snippets/pagination-response.mdx deleted file mode 100644 index 16bc822..0000000 --- a/snippets/pagination-response.mdx +++ /dev/null @@ -1,14 +0,0 @@ -List endpoints return a paginated response: - -```json -{ - "data": [], - "meta": { - "previousCursor": "string or null", - "nextCursor": "string or null", - "total": 42 - } -} -``` - -Pass `nextCursor` as a query parameter to fetch the next page. See [Pagination](/guides/principles/pagination). diff --git a/snippets/version-header.mdx b/snippets/version-header.mdx deleted file mode 100644 index 0781ab3..0000000 --- a/snippets/version-header.mdx +++ /dev/null @@ -1,5 +0,0 @@ - - Include the `X-Trace-Version` header to pin your integration to a specific - API version. If omitted, the default version is used. - See [Versioning](/guides/principles/versioning). - diff --git a/webhooks/overview.mdx b/webhooks/overview.mdx index 4212b9c..040cc1b 100644 --- a/webhooks/overview.mdx +++ b/webhooks/overview.mdx @@ -48,7 +48,7 @@ Other headers you can rely on: | --- | --- | | `X-Message-Id` | Unique delivery identifier — also a UUID for idempotency on your side | | `X-Company-Id` | Your Trace company identifier | -| `X-Event-Type` | Event type (e.g., `OPERATION_REQUESTED`) | +| `X-Event-Type` | Event type (e.g., `operation.requested`) | | `X-Resource-Name` | Resource group (e.g., `OPERATION`) | See [Verify signatures](/webhooks/verify-signatures) for verification code in Python, JavaScript, and Go. @@ -65,8 +65,16 @@ Browse the full event catalog by resource: | Resource | Events | Description | | --- | --- | --- | -| [Account](/api-reference/fx-webhook/events/account/asset-activated) | 1 | Account asset onboarding completion | -| [Operation](/api-reference/fx-webhook/events/operation/operation-requested) | 1 | Payment operation lifecycle | -| [Beneficiary](/api-reference/fx-webhook/events/beneficiary/beneficiary-instruction-created) | 3 | Per-instruction creation, approval, and rejection | +| [Account](/api-reference/fx-webhook/events/account/asset-activated) | 1 | Asset onboarding completion | +| [Operation](/api-reference/fx-webhook/events/operation/operation-requested) | 1 | Operation creation | +| [Beneficiary](/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created) | 3 | Payment instruction creation, approval, and rejection | You can also fetch the catalog programmatically: [`GET /references/ResourceName/all`](/api-reference/fx-webhook/subscriptions/list-resource-references). + + + Webhook events fire on **creation and review outcomes only**. Operation + lifecycle transitions (`PROCESSING`, `COMPLETED`, `FAILED`, `ON_HOLD`, + `ACTION_REQUIRED`) and account state changes are **not** published as + webhooks — poll `GET /api/operations/{operationId}` or + `GET /api/accounts/{accountId}` to track them. + diff --git a/webhooks/subscribe.mdx b/webhooks/subscribe.mdx index a8d16b1..46cf327 100644 --- a/webhooks/subscribe.mdx +++ b/webhooks/subscribe.mdx @@ -5,7 +5,7 @@ description: "Register webhook endpoints, scope event types, and manage subscrip ## Overview -A **subscription** is the binding between a partner-controlled URL and one or more resources (optionally narrowed to specific event types per resource). You create subscriptions through the [Subscriptions API](/api-reference/fx-webhook/subscriptions/create-subscription) and can have several active at once — typically one per environment. +A **subscription** is the binding between a customer-controlled URL and one or more resources (optionally narrowed to specific event types per resource). You create subscriptions through the [Subscriptions API](/api-reference/fx-webhook/subscriptions/create-subscription) and can have several active at once — typically one per environment. ## Create a subscription @@ -61,7 +61,7 @@ curl --request POST \ "resources": [ { "name": "BENEFICIARY", - "events": ["BENEFICIARY_INSTRUCTION_APPROVED", "BENEFICIARY_INSTRUCTION_REJECTED"] + "events": ["beneficiary.paymentInstruction.approved", "beneficiary.paymentInstruction.rejected"] } ], "allowRetry": true @@ -95,7 +95,7 @@ curl --request PATCH \ --data '{ "resources": [ { "name": "OPERATION", "includeAll": true }, - { "name": "BENEFICIARY", "events": ["BENEFICIARY_INSTRUCTION_APPROVED"] } + { "name": "BENEFICIARY", "events": ["beneficiary.paymentInstruction.approved"] } ] }' ``` diff --git a/webhooks/verify-signatures.mdx b/webhooks/verify-signatures.mdx index 8d02605..b7a72bc 100644 --- a/webhooks/verify-signatures.mdx +++ b/webhooks/verify-signatures.mdx @@ -89,7 +89,7 @@ Use a constant-time comparison (`hmac.compare_digest`, `crypto.timingSafeEqual`, | --- | --- | | `X-Message-Id` | Unique UUID per delivery attempt. Use it for idempotent processing. | | `X-Company-Id` | Your Trace company identifier. | -| `X-Event-Type` | Event type, e.g., `OPERATION_REQUESTED`. | +| `X-Event-Type` | Event type, e.g., `operation.requested`. | | `X-Resource-Name` | Resource group, e.g., `OPERATION`. | | `X-Message-Signature` | Hex-encoded HMAC-SHA256 of `messageId+clientId`. | From 8bcfd55edf3e642d563a0e6c8d500ef2521b3e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Alvial?= Date: Wed, 29 Apr 2026 11:44:29 -0300 Subject: [PATCH 2/5] docs(webhooks): revert event-type casing to SCREAMING_UPPER_CASE The audit pass standardized X-Event-Type to dot.lowercase per Stripe/Svix convention. Reverting to match the live backend's existing pattern (ACCOUNT_ASSET_ACTIVATED, BENEFICIARY_PAYMENT_INSTRUCTION_*, OPERATION_REQUESTED) and the project's own enum-naming rule in .claude/rules/openapi.md. Updates the EventType enum and webhook keys in apis/fx-webhook/openapi.yml, the five MDX event reference pages (titles + openapi refs), the three webhooks narrative pages (overview, subscribe, verify-signatures), and the three journey pages. --- .../events/account/asset-activated.mdx | 4 +- ...neficiary-payment-instruction-approved.mdx | 4 +- ...eneficiary-payment-instruction-created.mdx | 4 +- ...neficiary-payment-instruction-rejected.mdx | 4 +- .../events/operation/operation-requested.mdx | 4 +- apis/fx-webhook/openapi.yml | 38 +++++++++---------- journeys/deposit.mdx | 2 +- journeys/swap.mdx | 2 +- journeys/withdrawal.mdx | 2 +- webhooks/overview.mdx | 2 +- webhooks/subscribe.mdx | 4 +- webhooks/verify-signatures.mdx | 2 +- 12 files changed, 36 insertions(+), 36 deletions(-) diff --git a/api-reference/fx-webhook/events/account/asset-activated.mdx b/api-reference/fx-webhook/events/account/asset-activated.mdx index dd5fc99..6687546 100644 --- a/api-reference/fx-webhook/events/account/asset-activated.mdx +++ b/api-reference/fx-webhook/events/account/asset-activated.mdx @@ -1,5 +1,5 @@ --- -title: "account.asset.activated" +title: "ACCOUNT_ASSET_ACTIVATED" description: "Fires when an account asset finishes onboarding and funding instructions are available." -openapi: "apis/fx-webhook/openapi.yml webhook account.asset.activated" +openapi: "apis/fx-webhook/openapi.yml webhook ACCOUNT_ASSET_ACTIVATED" --- diff --git a/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-approved.mdx b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-approved.mdx index 6bf9d19..a14249b 100644 --- a/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-approved.mdx +++ b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-approved.mdx @@ -1,5 +1,5 @@ --- -title: "beneficiary.paymentInstruction.approved" +title: "BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED" description: "Fires when a payment instruction on a beneficiary is approved; check instructions[].status to find the one that transitioned." -openapi: "apis/fx-webhook/openapi.yml webhook beneficiary.paymentInstruction.approved" +openapi: "apis/fx-webhook/openapi.yml webhook BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED" --- diff --git a/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created.mdx b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created.mdx index fedee9f..7de29bb 100644 --- a/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created.mdx +++ b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created.mdx @@ -1,5 +1,5 @@ --- -title: "beneficiary.paymentInstruction.created" +title: "BENEFICIARY_PAYMENT_INSTRUCTION_CREATED" description: "Fires when a payment instruction is added to a beneficiary; the new instruction starts in PENDING_REVIEW." -openapi: "apis/fx-webhook/openapi.yml webhook beneficiary.paymentInstruction.created" +openapi: "apis/fx-webhook/openapi.yml webhook BENEFICIARY_PAYMENT_INSTRUCTION_CREATED" --- diff --git a/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected.mdx b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected.mdx index ec27716..147f1c0 100644 --- a/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected.mdx +++ b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected.mdx @@ -1,5 +1,5 @@ --- -title: "beneficiary.paymentInstruction.rejected" +title: "BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED" description: "Fires when a payment instruction on a beneficiary is rejected; check instructions[].status to find the one that transitioned." -openapi: "apis/fx-webhook/openapi.yml webhook beneficiary.paymentInstruction.rejected" +openapi: "apis/fx-webhook/openapi.yml webhook BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED" --- diff --git a/api-reference/fx-webhook/events/operation/operation-requested.mdx b/api-reference/fx-webhook/events/operation/operation-requested.mdx index da50b04..bb99618 100644 --- a/api-reference/fx-webhook/events/operation/operation-requested.mdx +++ b/api-reference/fx-webhook/events/operation/operation-requested.mdx @@ -1,5 +1,5 @@ --- -title: "operation.requested" +title: "OPERATION_REQUESTED" description: "Fires when a payment operation (deposit, withdrawal, swap) is created." -openapi: "apis/fx-webhook/openapi.yml webhook operation.requested" +openapi: "apis/fx-webhook/openapi.yml webhook OPERATION_REQUESTED" --- diff --git a/apis/fx-webhook/openapi.yml b/apis/fx-webhook/openapi.yml index 9a364fa..5e97b5c 100644 --- a/apis/fx-webhook/openapi.yml +++ b/apis/fx-webhook/openapi.yml @@ -142,12 +142,12 @@ components: type: string description: The specific event type within a resource, sent as the `X-Event-Type` header on each delivery. enum: - - account.asset.activated - - beneficiary.paymentInstruction.created - - beneficiary.paymentInstruction.approved - - beneficiary.paymentInstruction.rejected - - operation.requested - example: operation.requested + - ACCOUNT_ASSET_ACTIVATED + - BENEFICIARY_PAYMENT_INSTRUCTION_CREATED + - BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED + - BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED + - OPERATION_REQUESTED + example: OPERATION_REQUESTED ExecutionLogStatus: type: string enum: @@ -196,8 +196,8 @@ components: items: $ref: "#/components/schemas/EventType" example: - - beneficiary.paymentInstruction.approved - - beneficiary.paymentInstruction.rejected + - BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED + - BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED required: - name SubscriptionResource: @@ -1020,8 +1020,8 @@ components: type: object description: > Payload delivered for beneficiary payment-instruction lifecycle events - (`beneficiary.paymentInstruction.created`, `beneficiary.paymentInstruction.approved`, - `beneficiary.paymentInstruction.rejected`). + (`BENEFICIARY_PAYMENT_INSTRUCTION_CREATED`, `BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED`, + `BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED`). Review status is tracked **per payment instruction**, not on the beneficiary itself. The payload always carries the full beneficiary @@ -1057,7 +1057,7 @@ components: type: object description: > Payload delivered when an operation is first created - (`operation.requested`). Subsequent state transitions + (`OPERATION_REQUESTED`). Subsequent state transitions (`PROCESSING`, `COMPLETED`, `FAILED`, etc.) are not published as webhook events — poll `GET /api/operations/{operationId}` to track lifecycle progress. @@ -1144,8 +1144,8 @@ paths: resources: - name: BENEFICIARY events: - - beneficiary.paymentInstruction.approved - - beneficiary.paymentInstruction.rejected + - BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED + - BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED allowRetry: true responses: "201": @@ -1252,7 +1252,7 @@ paths: includeAll: true - name: BENEFICIARY events: - - beneficiary.paymentInstruction.approved + - BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED disable-retry: summary: Stop retrying failed deliveries value: @@ -1433,7 +1433,7 @@ paths: $ref: "#/components/schemas/ResourceReference" webhooks: - account.asset.activated: + ACCOUNT_ASSET_ACTIVATED: post: summary: Account asset activated description: Fires when an account asset finishes onboarding and funding instructions are available. @@ -1450,7 +1450,7 @@ webhooks: $ref: "#/components/responses/WebhookAck" "401": $ref: "#/components/responses/UnauthorizedError" - beneficiary.paymentInstruction.created: + BENEFICIARY_PAYMENT_INSTRUCTION_CREATED: post: summary: Beneficiary instruction created description: > @@ -1473,7 +1473,7 @@ webhooks: $ref: "#/components/responses/WebhookAck" "401": $ref: "#/components/responses/UnauthorizedError" - beneficiary.paymentInstruction.approved: + BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED: post: summary: Beneficiary instruction approved description: > @@ -1495,7 +1495,7 @@ webhooks: $ref: "#/components/responses/WebhookAck" "401": $ref: "#/components/responses/UnauthorizedError" - beneficiary.paymentInstruction.rejected: + BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED: post: summary: Beneficiary instruction rejected description: > @@ -1518,7 +1518,7 @@ webhooks: "401": $ref: "#/components/responses/UnauthorizedError" - operation.requested: + OPERATION_REQUESTED: post: summary: Operation requested description: Fires when a payment operation (deposit, withdrawal, swap) is created. diff --git a/journeys/deposit.mdx b/journeys/deposit.mdx index 80cd9a7..f2854c0 100644 --- a/journeys/deposit.mdx +++ b/journeys/deposit.mdx @@ -61,7 +61,7 @@ Deposits credit an account when funds arrive via the chosen funding rail. The cu - Poll `GET /api/operations/{operationId}` until the inbound payment is reconciled and the operation transitions to `COMPLETED`. Lifecycle transitions are not published as webhook events; the `operation.requested` webhook only fires on creation. + Poll `GET /api/operations/{operationId}` until the inbound payment is reconciled and the operation transitions to `COMPLETED`. Lifecycle transitions are not published as webhook events; the `OPERATION_REQUESTED` webhook only fires on creation. ```bash curl --request GET \ diff --git a/journeys/swap.mdx b/journeys/swap.mdx index c68fc37..2afd81f 100644 --- a/journeys/swap.mdx +++ b/journeys/swap.mdx @@ -54,7 +54,7 @@ Swaps convert funds between assets within the same account — for example, BRL - Poll `GET /api/operations/{operationId}` to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). Lifecycle transitions are not published as webhook events; the `operation.requested` webhook only fires on creation. + Poll `GET /api/operations/{operationId}` to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). Lifecycle transitions are not published as webhook events; the `OPERATION_REQUESTED` webhook only fires on creation. ```bash curl --request GET \ diff --git a/journeys/withdrawal.mdx b/journeys/withdrawal.mdx index 22e1a90..ab9f24e 100644 --- a/journeys/withdrawal.mdx +++ b/journeys/withdrawal.mdx @@ -96,7 +96,7 @@ Withdrawals move funds out of an account to an external destination — a bank a - Poll `GET /api/operations/{operationId}` to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). Compliance review can pause the operation in `ON_HOLD` or `ACTION_REQUIRED` until additional checks clear. Lifecycle transitions are not published as webhook events; the `operation.requested` webhook only fires on creation. + Poll `GET /api/operations/{operationId}` to follow the operation through `PROCESSING` to `COMPLETED` (or `FAILED`). Compliance review can pause the operation in `ON_HOLD` or `ACTION_REQUIRED` until additional checks clear. Lifecycle transitions are not published as webhook events; the `OPERATION_REQUESTED` webhook only fires on creation. ```bash curl --request GET \ diff --git a/webhooks/overview.mdx b/webhooks/overview.mdx index 040cc1b..4d7f626 100644 --- a/webhooks/overview.mdx +++ b/webhooks/overview.mdx @@ -48,7 +48,7 @@ Other headers you can rely on: | --- | --- | | `X-Message-Id` | Unique delivery identifier — also a UUID for idempotency on your side | | `X-Company-Id` | Your Trace company identifier | -| `X-Event-Type` | Event type (e.g., `operation.requested`) | +| `X-Event-Type` | Event type (e.g., `OPERATION_REQUESTED`) | | `X-Resource-Name` | Resource group (e.g., `OPERATION`) | See [Verify signatures](/webhooks/verify-signatures) for verification code in Python, JavaScript, and Go. diff --git a/webhooks/subscribe.mdx b/webhooks/subscribe.mdx index 46cf327..4921f65 100644 --- a/webhooks/subscribe.mdx +++ b/webhooks/subscribe.mdx @@ -61,7 +61,7 @@ curl --request POST \ "resources": [ { "name": "BENEFICIARY", - "events": ["beneficiary.paymentInstruction.approved", "beneficiary.paymentInstruction.rejected"] + "events": ["BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED", "BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED"] } ], "allowRetry": true @@ -95,7 +95,7 @@ curl --request PATCH \ --data '{ "resources": [ { "name": "OPERATION", "includeAll": true }, - { "name": "BENEFICIARY", "events": ["beneficiary.paymentInstruction.approved"] } + { "name": "BENEFICIARY", "events": ["BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED"] } ] }' ``` diff --git a/webhooks/verify-signatures.mdx b/webhooks/verify-signatures.mdx index b7a72bc..8d02605 100644 --- a/webhooks/verify-signatures.mdx +++ b/webhooks/verify-signatures.mdx @@ -89,7 +89,7 @@ Use a constant-time comparison (`hmac.compare_digest`, `crypto.timingSafeEqual`, | --- | --- | | `X-Message-Id` | Unique UUID per delivery attempt. Use it for idempotent processing. | | `X-Company-Id` | Your Trace company identifier. | -| `X-Event-Type` | Event type, e.g., `operation.requested`. | +| `X-Event-Type` | Event type, e.g., `OPERATION_REQUESTED`. | | `X-Resource-Name` | Resource group, e.g., `OPERATION`. | | `X-Message-Signature` | Hex-encoded HMAC-SHA256 of `messageId+clientId`. | From 53869a1f939af1d66629b928f8f4a532a32df7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Alvial?= Date: Wed, 29 Apr 2026 11:44:38 -0300 Subject: [PATCH 3/5] docs(api): simplify TaxId to value+type and fix description YAML Drops `country` from TaxIdRequest and TaxIdResponse in both fx-account and fx-payment. The `type` field already implies the country (CPF/CNPJ are Brazilian, CUIT/CUIL are Argentine, RUT_CL is Chilean, etc.), so country was redundant. Updates the schemas, the eight affected examples, the TaxIdType description, and the "country/type-specific validator" / "type and country" error prose. Also fixes a pre-existing YAML bug in fx-payment around line 2197 where a `description: >` block was emptied and the actual description text had slipped under `headers:`, breaking `mint validate`. --- apis/fx-account/openapi.yml | 29 ++++++++++------------------- apis/fx-payment/openapi.yml | 14 ++------------ 2 files changed, 12 insertions(+), 31 deletions(-) diff --git a/apis/fx-account/openapi.yml b/apis/fx-account/openapi.yml index 6b24d24..865117e 100644 --- a/apis/fx-account/openapi.yml +++ b/apis/fx-account/openapi.yml @@ -252,7 +252,7 @@ components: - address TaxIdRequest: type: object - description: Tax identifier for the owner or beneficial owner. Validated against the country and type. + description: Tax identifier for the owner or beneficial owner. Validated against the type-specific format rules. properties: value: type: string @@ -260,12 +260,9 @@ components: example: "11222333000181" type: $ref: "#/components/schemas/TaxIdType" - country: - $ref: "#/components/schemas/Country" required: - value - type - - country AddressRequest: type: object properties: @@ -562,12 +559,9 @@ components: example: "11222333000181" type: $ref: "#/components/schemas/TaxIdType" - country: - $ref: "#/components/schemas/Country" required: - value - type - - country AddressResponse: type: object properties: @@ -820,8 +814,9 @@ components: TaxIdType: type: string description: > - Country-specific tax identifier type. The `country` field on the same - TaxId object must match the country implied by the type. + Country-specific tax identifier type. The type itself implies the + country (`CPF`/`CNPJ` are Brazilian, `CUIT`/`CUIL` are Argentine, + `RUT_CL` is Chilean, etc.). enum: - CPF - CNPJ @@ -1553,7 +1548,6 @@ paths: taxId: value: "11222333000181" type: "CNPJ" - country: "BR" industry: "INFORMATION_TECHNOLOGY" incorporateDate: "2018-05-12" address: @@ -1576,7 +1570,6 @@ paths: taxId: value: "12345678909" type: "CPF" - country: "BR" birthDate: "1990-03-21" address: addressLine1: "Rua das Flores, 100" @@ -1605,14 +1598,14 @@ paths: $ref: "#/components/schemas/ErrorResponse" examples: invalidTaxId: - summary: Owner taxId fails the country/type-specific validator + summary: Owner taxId fails the type-specific validator value: code: "INVALID_DATA" message: "Object contains invalid data" details: errors: - code: "VALIDATE_TAX_ID" - message: "Tax ID is invalid for the given type and country" + message: "Tax ID is invalid for the given type" field: "body:owner.taxId.value" params: {} unsupportedAsset: @@ -2135,7 +2128,6 @@ paths: taxId: value: "12345678909" type: "CPF" - country: "BR" address: addressLine1: "Rua das Flores, 100" city: "São Paulo" @@ -2164,14 +2156,14 @@ paths: $ref: "#/components/schemas/ErrorResponse" examples: invalidTaxId: - summary: UBO taxId fails the country/type-specific validator + summary: UBO taxId fails the type-specific validator value: code: "INVALID_DATA" message: "Object contains invalid data" details: errors: - code: "VALIDATE_TAX_ID" - message: "Tax ID is invalid for the given type and country" + message: "Tax ID is invalid for the given type" field: "body:taxId.value" params: {} invalidData: @@ -2397,7 +2389,6 @@ paths: taxId: value: "98765432100" type: "CPF" - country: "BR" address: addressLine1: "Av. Paulista, 1000" city: "São Paulo" @@ -2426,14 +2417,14 @@ paths: $ref: "#/components/schemas/ErrorResponse" examples: invalidTaxId: - summary: UBO taxId fails the country/type-specific validator + summary: UBO taxId fails the type-specific validator value: code: "INVALID_DATA" message: "Object contains invalid data" details: errors: - code: "VALIDATE_TAX_ID" - message: "Tax ID is invalid for the given type and country" + message: "Tax ID is invalid for the given type" field: "body:taxId.value" params: {} invalidData: diff --git a/apis/fx-payment/openapi.yml b/apis/fx-payment/openapi.yml index 8393b17..33b53b3 100644 --- a/apis/fx-payment/openapi.yml +++ b/apis/fx-payment/openapi.yml @@ -328,12 +328,9 @@ components: Tax identifier kind. Brazilian beneficiaries use `CPF` (individual) or `CNPJ` (company); other jurisdictions use the local code (e.g. `SSN`, `EIN`, `CUIT`). example: "CPF" - country: - $ref: "#/components/schemas/Country" required: - value - type - - country TaxIdResponse: type: object description: Tax identifier returned for a beneficiary holder. @@ -344,12 +341,9 @@ components: type: type: string example: "CPF" - country: - $ref: "#/components/schemas/Country" required: - value - type - - country Country: type: string description: ISO 3166-1 alpha-2 country code. @@ -2176,7 +2170,6 @@ paths: taxId: value: "52998224725" type: "CPF" - country: "BR" dateOfBirth: "1990-01-15" address: addressLine1: "Rua Augusta, 500" @@ -2202,11 +2195,11 @@ paths: updatedAt: "2026-04-25T02:39:17Z" "400": description: > + Validation error: missing or malformed fields, inline beneficiary not supported, or + inline instruction not supported. headers: X-Request-Id: $ref: "#/components/headers/RequestId" - Validation error: missing or malformed fields, inline beneficiary not supported, or - inline instruction not supported. content: application/json: schema: @@ -2981,7 +2974,6 @@ paths: taxId: value: "52998224725" type: "CPF" - country: "BR" dateOfBirth: "1985-03-15" address: addressLine1: "Av. Paulista, 1000" @@ -3006,7 +2998,6 @@ paths: taxId: value: "27922482000193" type: "CNPJ" - country: "BR" address: addressLine1: "Rua da Consolação, 222" city: "São Paulo" @@ -3030,7 +3021,6 @@ paths: taxId: value: "11144477735" type: "CPF" - country: "BR" dateOfBirth: "1990-07-22" address: addressLine1: "123 Main St" From 2d9797fc583d9809272f01090ae7d5f5bd6b15a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Alvial?= Date: Wed, 29 Apr 2026 11:48:37 -0300 Subject: [PATCH 4/5] docs(api): switch Filters description to folded scalar The text-formatting eval flagged the `Filters` parameter description in all three specs because `description: |` (literal block) preserves soft wraps as visible breaks in Mintlify. Switching to `>` (folded scalar) collapses the soft wraps to spaces and renders as one paragraph. --- apis/fx-account/openapi.yml | 2 +- apis/fx-payment/openapi.yml | 2 +- apis/fx-webhook/openapi.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apis/fx-account/openapi.yml b/apis/fx-account/openapi.yml index 865117e..7f4a638 100644 --- a/apis/fx-account/openapi.yml +++ b/apis/fx-account/openapi.yml @@ -109,7 +109,7 @@ components: name: filters in: query required: false - description: | + description: > Filter expression using LHS Brackets syntax (`field[operator]=value`). Combine multiple conditions with `and(...)`, `or(...)`, or `;` (implicit AND). See the [Filtering](/guides/principles/filtering) guide for the full operator catalog and examples. diff --git a/apis/fx-payment/openapi.yml b/apis/fx-payment/openapi.yml index 33b53b3..e24bf4f 100644 --- a/apis/fx-payment/openapi.yml +++ b/apis/fx-payment/openapi.yml @@ -115,7 +115,7 @@ components: name: filters in: query required: false - description: | + description: > Filter expression using LHS Brackets syntax (`field[operator]=value`). Combine multiple conditions with `and(...)`, `or(...)`, or `;` (implicit AND). See the [Filtering](/guides/principles/filtering) guide for the full operator catalog and examples. diff --git a/apis/fx-webhook/openapi.yml b/apis/fx-webhook/openapi.yml index 5e97b5c..8bac515 100644 --- a/apis/fx-webhook/openapi.yml +++ b/apis/fx-webhook/openapi.yml @@ -107,7 +107,7 @@ components: name: filters in: query required: false - description: | + description: > Filter expression using LHS Brackets syntax (`field[operator]=value`). Combine multiple conditions with `and(...)`, `or(...)`, or `;` (implicit AND). See the [Filtering](/guides/principles/filtering) guide for the full operator catalog and examples. From 19284cfe327fecc6687e87c62231d493ac180453 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rio=20Alvial?= Date: Wed, 29 Apr 2026 12:06:43 -0300 Subject: [PATCH 5/5] docs(api): tighten reuse and fix webhook 401 / taxId examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fx-webhook: drop 401 responses from outbound webhook handlers (semantic bug — those POSTs are Trace→customer and never return Trace's auth envelope); update beneficiary event summaries to "Beneficiary payment instruction"; AmountEvent.asset now $refs Asset. - fx-payment: add TaxIdType enum (mirrors fx-account); switch TaxIdRequest/Response.type and four PaymentInstruction asset fields to $ref; add OperationList and BeneficiaryList; rewrite redundant getOperation description. - fx-account: add AccountList, FundingInstructionList, UBOList; rewrite inline list responses as $ref; copy Asset precision-table description from fx-payment. - journeys/withdrawal: drop country from taxId example (field was removed from the spec). - guides/principles/datetime: bump 2025 example to 2026. - .claude/rules/openapi.md: remove stale CONTRIBUTING.md reference. --- .claude/rules/openapi.md | 2 +- apis/fx-account/openapi.yml | 88 ++++++++++++-------- apis/fx-payment/openapi.yml | 142 +++++++++++++++++++++++---------- apis/fx-webhook/openapi.yml | 24 ++---- guides/principles/datetime.mdx | 2 +- journeys/withdrawal.mdx | 3 +- 6 files changed, 162 insertions(+), 99 deletions(-) diff --git a/.claude/rules/openapi.md b/.claude/rules/openapi.md index 3e0de5e..18a6d24 100644 --- a/.claude/rules/openapi.md +++ b/.claude/rules/openapi.md @@ -80,7 +80,7 @@ machine-checkable subset. - Server URL is per-service: `https://faas.{env}.tracefinance.io/account`, `/payment`, `/webhook`. - Strip `/dashboard` and `/admin` paths — only `/api` channel is - documented (per `CONTRIBUTING.md`). Public discovery endpoints (e.g., + documented. Public discovery endpoints (e.g., `/references/...`) sit outside `/api` and require no auth — set `security: []` to override the global `bearerAuth`. diff --git a/apis/fx-account/openapi.yml b/apis/fx-account/openapi.yml index 7f4a638..cf5a298 100644 --- a/apis/fx-account/openapi.yml +++ b/apis/fx-account/openapi.yml @@ -389,6 +389,18 @@ components: - states - createdAt - updatedAt + AccountList: + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/AccountResponse" + meta: + $ref: "#/components/schemas/PageMetadata" + required: + - data + - meta AssetResponse: type: object description: An asset available on the account and the onboarding status of its underlying provider account. @@ -788,7 +800,6 @@ components: example: "IDENTITY_VERIFICATION" Asset: type: string - description: ISO 4217 currency code or stablecoin ticker. enum: - USD - BRL @@ -810,6 +821,21 @@ components: - USDT - EURC - BRLT + description: | + ISO 4217 currency code or stablecoin ticker. The maximum decimal scale of an `AmountValue` paired with this asset depends on the asset: + + | Asset | Decimals | Asset | Decimals | + |---|---|---|---| + | USD | 2 | NZD | 2 | + | BRL | 2 | CHF | 2 | + | EUR | 2 | NOK | 2 | + | CAD | 2 | DKK | 2 | + | AUD | 2 | PLN | 2 | + | GBP | 2 | SEK | 2 | + | MXN | 2 | USDC | 6 | + | HKD | 2 | USDT | 6 | + | SGD | 2 | EURC | 6 | + | PHP | 2 | BRLT | 6 | example: "BRL" TaxIdType: type: string @@ -1170,6 +1196,18 @@ components: - taxId - address - currentState + UBOList: + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/UBOResponse" + meta: + $ref: "#/components/schemas/PageMetadata" + required: + - data + - meta Rail: type: string description: Payment rail used to fund a deposit. @@ -1328,6 +1366,18 @@ components: required: - network - walletAddress + FundingInstructionList: + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/FundingInstructionResponse" + meta: + $ref: "#/components/schemas/PageMetadata" + required: + - data + - meta UploadDocumentRequest: type: object description: > @@ -1686,17 +1736,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: array - items: - $ref: "#/components/schemas/AccountResponse" - meta: - $ref: "#/components/schemas/PageMetadata" - required: - - data - - meta + $ref: "#/components/schemas/AccountList" "401": $ref: "#/components/responses/UnauthorizedError" @@ -1857,17 +1897,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: array - items: - $ref: "#/components/schemas/FundingInstructionResponse" - meta: - $ref: "#/components/schemas/PageMetadata" - required: - - data - - meta + $ref: "#/components/schemas/FundingInstructionList" examples: mixed: summary: PIX and TED instructions @@ -2263,17 +2293,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: array - items: - $ref: "#/components/schemas/UBOResponse" - meta: - $ref: "#/components/schemas/PageMetadata" - required: - - data - - meta + $ref: "#/components/schemas/UBOList" "401": $ref: "#/components/responses/UnauthorizedError" "404": diff --git a/apis/fx-payment/openapi.yml b/apis/fx-payment/openapi.yml index e24bf4f..7da51e2 100644 --- a/apis/fx-payment/openapi.yml +++ b/apis/fx-payment/openapi.yml @@ -311,6 +311,73 @@ components: - INDIVIDUAL - COMPANY description: Whether a beneficiary or account holder is a person (CPF) or a company (CNPJ). + TaxIdType: + type: string + description: > + Country-specific tax identifier type. The type itself implies the + country (`CPF`/`CNPJ` are Brazilian, `CUIT`/`CUIL` are Argentine, + `RUT_CL` is Chilean, etc.). + enum: + - CPF + - CNPJ + - CUIT + - CUIL + - RUT_CL + - RUT_UY + - CI_UY + - RUC + - DNI_PE + - NIT + - CC + - RFC + - RUC_PY + - CI_PY + - NIPC + - NIF_PT + - CIF + - NIF_ES + - NIE + - SIREN + - SIRET + - NIF_FR + - PARTITA_IVA + - CODICE_FISCALE + - UST_IDNR + - STEUER_ID + - KVK + - BSN + - BCE + - NN_BE + - ORGANISATIONSNUMMER + - PERSONNUMMER + - ORGANISASJONSNUMMER + - FODSELSNUMMER + - UID_CH + - AHV + - VAT_GB + - NIN + - EIN + - SSN + - ITIN + - BN_CA + - SIN + - ABN + - TFN + - USCC + - CORPORATE_NUMBER_JP + - MY_NUMBER + - BRN_KR + - RRN + - GSTIN + - PAN + - INN_RU + - COMPANY_NUMBER_IL + - TEUDAT_ZEHUT + - TRADE_LICENSE + - EMIRATES_ID + - CIPC + - SA_ID + example: "CPF" TaxIdRequest: type: object description: > @@ -323,11 +390,7 @@ components: description: Tax identifier digits only (no dots, dashes, or slashes). example: "52998224725" type: - type: string - description: > - Tax identifier kind. Brazilian beneficiaries use `CPF` (individual) or `CNPJ` - (company); other jurisdictions use the local code (e.g. `SSN`, `EIN`, `CUIT`). - example: "CPF" + $ref: "#/components/schemas/TaxIdType" required: - value - type @@ -339,8 +402,7 @@ components: type: string example: "52998224725" type: - type: string - example: "CPF" + $ref: "#/components/schemas/TaxIdType" required: - value - type @@ -1474,6 +1536,18 @@ components: - effectiveRate - expiresAt - consumedAt + OperationList: + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/OperationResponse" + meta: + $ref: "#/components/schemas/PageMetadata" + required: + - data + - meta # ── Beneficiary schemas ─────────────────────────────────────────── CreateBeneficiaryRequest: @@ -1584,9 +1658,7 @@ components: enum: - PIX asset: - type: string - description: Asset/currency for the payment instruction (e.g. `BRL`). - example: "BRL" + $ref: "#/components/schemas/Asset" dictKeyType: type: string enum: @@ -1615,9 +1687,7 @@ components: enum: - BANK_ACCOUNT asset: - type: string - description: Asset/currency for the payment instruction. - example: "BRL" + $ref: "#/components/schemas/Asset" bankCode: type: string description: COMPE code of the receiving bank. The server resolves it to the full `Bank` (name, ISPB) on the response side. @@ -1646,9 +1716,7 @@ components: enum: - CRYPTO_WALLET asset: - type: string - description: Crypto asset/currency. - example: "USDT" + $ref: "#/components/schemas/Asset" address: type: string description: Wallet address. @@ -1748,6 +1816,18 @@ components: - instructionCount - createdAt - updatedAt + BeneficiaryList: + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/BeneficiarySummaryResponse" + meta: + $ref: "#/components/schemas/PageMetadata" + required: + - data + - meta HolderResponse: description: The person or company that receives funds. Discriminated by `type`. oneOf: @@ -1962,9 +2042,7 @@ components: example: "pi-a1b2c3d4-5e6f-7890-abcd-ef1234567890" readOnly: true asset: - type: string - description: Asset/currency for this instruction. - example: "BRL" + $ref: "#/components/schemas/Asset" currentState: $ref: "#/components/schemas/InstructionState" address: @@ -2845,7 +2923,7 @@ paths: tags: - Operations summary: Get an operation - description: Retrieves the full details of an operation by its ID. + description: Retrieves an operation by its ID, including the consumed quote, beneficiary snapshot, payment instruction, and current state. parameters: - $ref: "#/components/parameters/OperationId" - $ref: "#/components/parameters/TraceVersion" @@ -2900,17 +2978,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: array - items: - $ref: "#/components/schemas/OperationResponse" - meta: - $ref: "#/components/schemas/PageMetadata" - required: - - data - - meta + $ref: "#/components/schemas/OperationList" "401": $ref: "#/components/responses/UnauthorizedError" "422": @@ -3098,17 +3166,7 @@ paths: content: application/json: schema: - type: object - properties: - data: - type: array - items: - $ref: "#/components/schemas/BeneficiarySummaryResponse" - meta: - $ref: "#/components/schemas/PageMetadata" - required: - - data - - meta + $ref: "#/components/schemas/BeneficiaryList" "401": $ref: "#/components/responses/UnauthorizedError" diff --git a/apis/fx-webhook/openapi.yml b/apis/fx-webhook/openapi.yml index 8bac515..2cdc7b8 100644 --- a/apis/fx-webhook/openapi.yml +++ b/apis/fx-webhook/openapi.yml @@ -463,9 +463,7 @@ components: description: Decimal amount in the asset's canonical scale. example: "500.00" asset: - type: string - description: Currency or stablecoin code (e.g., `BRL`, `USDT`). - example: "BRL" + $ref: "#/components/schemas/Asset" decimals: type: integer description: Number of decimal places for the asset. @@ -1448,11 +1446,9 @@ webhooks: responses: "200": $ref: "#/components/responses/WebhookAck" - "401": - $ref: "#/components/responses/UnauthorizedError" BENEFICIARY_PAYMENT_INSTRUCTION_CREATED: post: - summary: Beneficiary instruction created + summary: Beneficiary payment instruction created description: > Fires when a payment instruction is added to a beneficiary — both when the beneficiary itself is first created and when an additional @@ -1471,11 +1467,9 @@ webhooks: responses: "200": $ref: "#/components/responses/WebhookAck" - "401": - $ref: "#/components/responses/UnauthorizedError" BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED: post: - summary: Beneficiary instruction approved + summary: Beneficiary payment instruction approved description: > Fires when one of the beneficiary's payment instructions is approved and becomes usable. Review status lives on each instruction; check @@ -1493,11 +1487,9 @@ webhooks: responses: "200": $ref: "#/components/responses/WebhookAck" - "401": - $ref: "#/components/responses/UnauthorizedError" BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED: post: - summary: Beneficiary instruction rejected + summary: Beneficiary payment instruction rejected description: > Fires when one of the beneficiary's payment instructions is rejected in compliance review and cannot be used. Review status lives on each @@ -1515,9 +1507,6 @@ webhooks: responses: "200": $ref: "#/components/responses/WebhookAck" - - "401": - $ref: "#/components/responses/UnauthorizedError" OPERATION_REQUESTED: post: summary: Operation requested @@ -1532,7 +1521,4 @@ webhooks: $ref: "#/components/schemas/OperationEvent" responses: "200": - $ref: "#/components/responses/WebhookAck" - - "401": - $ref: "#/components/responses/UnauthorizedError" \ No newline at end of file + $ref: "#/components/responses/WebhookAck" \ No newline at end of file diff --git a/guides/principles/datetime.mdx b/guides/principles/datetime.mdx index 7094883..be575d9 100644 --- a/guides/principles/datetime.mdx +++ b/guides/principles/datetime.mdx @@ -19,7 +19,7 @@ Example: `2026-04-22T20:00:00Z` | Component | Description | Example | | --- | --- | --- | -| `yyyy` | Four-digit year | `2025` | +| `yyyy` | Four-digit year | `2026` | | `MM` | Two-digit month (01–12) | `01` | | `dd` | Two-digit day (01–31) | `15` | | `T` | Date/time separator | `T` | diff --git a/journeys/withdrawal.mdx b/journeys/withdrawal.mdx index ab9f24e..31a5cec 100644 --- a/journeys/withdrawal.mdx +++ b/journeys/withdrawal.mdx @@ -31,8 +31,7 @@ Withdrawals move funds out of an account to an external destination — a bank a "lastName": "Doe", "taxId": { "value": "12345678901", - "type": "CPF", - "country": "BR" + "type": "CPF" }, "dateOfBirth": "1990-01-15" },