diff --git a/.claude/rules/openapi.md b/.claude/rules/openapi.md
index 0d141b7..18a6d24 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
@@ -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`.
@@ -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/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-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 51%
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..a14249b 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_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.instruction.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
new file mode 100644
index 0000000..7de29bb
--- /dev/null
+++ b/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created.mdx
@@ -0,0 +1,5 @@
+---
+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_PAYMENT_INSTRUCTION_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 51%
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..147f1c0 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_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.instruction.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-account/openapi.yml b/apis/fx-account/openapi.yml
index 410d7b9..cf5a298 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.
@@ -243,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
@@ -251,12 +260,9 @@ components:
example: "11222333000181"
type:
$ref: "#/components/schemas/TaxIdType"
- country:
- $ref: "#/components/schemas/Country"
required:
- value
- type
- - country
AddressRequest:
type: object
properties:
@@ -333,6 +339,7 @@ components:
type: string
format: uuid
example: "a1b2c3d4-5e6f-7890-abcd-ef1234567890"
+ readOnly: true
sourceAccountId:
type: string
format: uuid
@@ -365,10 +372,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
@@ -380,12 +389,24 @@ 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.
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 +421,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 +496,7 @@ components:
type: string
description: Customer identifier.
example: "c1a2b3c4-d5e6-7890-abcd-ef1234567890"
+ readOnly: true
required:
- id
OwnerResponse:
@@ -553,12 +571,9 @@ components:
example: "11222333000181"
type:
$ref: "#/components/schemas/TaxIdType"
- country:
- $ref: "#/components/schemas/Country"
required:
- value
- type
- - country
AddressResponse:
type: object
properties:
@@ -783,9 +798,8 @@ components:
enum:
- IDENTITY_VERIFICATION
example: "IDENTITY_VERIFICATION"
- Currency:
+ Asset:
type: string
- description: Fiat or crypto currency code.
enum:
- USD
- BRL
@@ -807,12 +821,28 @@ 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
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
@@ -1145,6 +1175,7 @@ components:
type: string
format: uuid
example: "b2c3d4e5-f6a7-8901-bcde-f12345678901"
+ readOnly: true
name:
type: string
example: "João Silva"
@@ -1165,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.
@@ -1200,7 +1243,7 @@ components:
type: object
properties:
asset:
- $ref: "#/components/schemas/Currency"
+ $ref: "#/components/schemas/Asset"
rail:
$ref: "#/components/schemas/Rail"
required:
@@ -1323,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: >
@@ -1382,26 +1437,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 +1473,9 @@ components:
responses:
UnauthorizedError:
description: Missing or invalid authentication token.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -1485,16 +1543,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 +1559,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
@@ -1543,7 +1598,6 @@ paths:
taxId:
value: "11222333000181"
type: "CNPJ"
- country: "BR"
industry: "INFORMATION_TECHNOLOGY"
incorporateDate: "2018-05-12"
address:
@@ -1566,7 +1620,6 @@ paths:
taxId:
value: "12345678909"
type: "CPF"
- country: "BR"
birthDate: "1990-03-21"
address:
addressLine1: "Rua das Flores, 100"
@@ -1577,26 +1630,32 @@ 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:
$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:
@@ -1621,6 +1680,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 +1699,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,24 +1726,17 @@ 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:
- 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"
@@ -1695,12 +1753,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 +1776,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 +1801,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 +1820,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 +1844,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 +1883,45 @@ 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"
+ $ref: "#/components/schemas/FundingInstructionList"
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 +1933,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 +1965,9 @@ paths:
responses:
"200":
description: Account balance.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -1880,6 +1979,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 +1993,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 +2062,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 +2119,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:
@@ -2044,7 +2158,6 @@ paths:
taxId:
value: "12345678909"
type: "CPF"
- country: "BR"
address:
addressLine1: "Rua das Flores, 100"
city: "São Paulo"
@@ -2055,26 +2168,32 @@ 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:
$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:
@@ -2092,6 +2211,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 +2223,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 +2240,9 @@ paths:
taxId: "12345678900"
"422":
description: Business rule violation.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -2138,7 +2266,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 +2279,28 @@ 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"
+ $ref: "#/components/schemas/UBOList"
"401":
$ref: "#/components/responses/UnauthorizedError"
"404":
description: Account not found.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -2173,6 +2310,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 +2340,9 @@ paths:
responses:
"200":
description: Beneficial owner details.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -2208,6 +2351,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 +2365,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 +2389,7 @@ paths:
parameters:
- $ref: "#/components/parameters/AccountId"
- $ref: "#/components/parameters/UboId"
+ - $ref: "#/components/parameters/IdempotencyKey"
- $ref: "#/components/parameters/TraceVersion"
requestBody:
required: true
@@ -2259,7 +2409,6 @@ paths:
taxId:
value: "98765432100"
type: "CPF"
- country: "BR"
address:
addressLine1: "Av. Paulista, 1000"
city: "São Paulo"
@@ -2270,26 +2419,32 @@ 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:
$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:
@@ -2307,6 +2462,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 +2476,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 +2509,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 +2526,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 +2547,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..7da51e2 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,101 @@ components:
- INDIVIDUAL
- COMPANY
description: Whether a beneficiary or account holder is a person (CPF) or a company (CNPJ).
- TaxId:
+ 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: >
- 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:
- type:
+ value:
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:
+ description: Tax identifier digits only (no dots, dashes, or slashes).
+ example: "52998224725"
+ type:
+ $ref: "#/components/schemas/TaxIdType"
+ required:
+ - value
+ - type
+ 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:
+ $ref: "#/components/schemas/TaxIdType"
required:
+ - value
- type
- - number
Country:
type: string
description: ISO 3166-1 alpha-2 country code.
@@ -569,24 +671,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 +741,7 @@ components:
id:
type: string
format: uuid
+ readOnly: true
customerId:
type: string
format: uuid
@@ -743,12 +845,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 +920,7 @@ components:
tradeName:
type: string
taxId:
- $ref: "#/components/schemas/TaxId"
+ $ref: "#/components/schemas/TaxIdRequest"
dateOfBirth:
type: string
format: date
@@ -885,6 +987,7 @@ components:
id:
type: string
format: uuid
+ readOnly: true
customerId:
type: string
format: uuid
@@ -916,9 +1019,11 @@ components:
createdAt:
type: string
format: date-time
+ readOnly: true
updatedAt:
type: string
format: date-time
+ readOnly: true
required:
- id
- customerId
@@ -955,8 +1060,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 +1304,7 @@ components:
lastName:
type: string
taxId:
- $ref: "#/components/schemas/TaxId"
+ $ref: "#/components/schemas/TaxIdResponse"
dateOfBirth:
type: string
format: date
@@ -1229,7 +1334,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:
@@ -1431,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:
@@ -1478,7 +1595,7 @@ components:
description: Family name.
example: "Silva"
taxId:
- $ref: "#/components/schemas/TaxId"
+ $ref: "#/components/schemas/TaxIdRequest"
dateOfBirth:
type: string
format: date
@@ -1511,7 +1628,7 @@ components:
description: Trade name. Optional.
example: "Acme"
taxId:
- $ref: "#/components/schemas/TaxId"
+ $ref: "#/components/schemas/TaxIdRequest"
address:
$ref: "#/components/schemas/Address"
required:
@@ -1541,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:
@@ -1572,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.
@@ -1603,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.
@@ -1627,6 +1738,7 @@ components:
type: string
format: uuid
example: "ben-a1b2c3d4-5e6f-7890-abcd-ef1234567890"
+ readOnly: true
customerId:
type: string
format: uuid
@@ -1647,10 +1759,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 +1781,7 @@ components:
type: string
format: uuid
example: "ben-a1b2c3d4-5e6f-7890-abcd-ef1234567890"
+ readOnly: true
customerId:
type: string
format: uuid
@@ -1687,10 +1802,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
@@ -1699,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:
@@ -1724,7 +1853,7 @@ components:
type: string
example: "Silva"
taxId:
- $ref: "#/components/schemas/TaxId"
+ $ref: "#/components/schemas/TaxIdResponse"
dateOfBirth:
type: string
format: date
@@ -1755,7 +1884,7 @@ components:
nullable: true
example: "Acme"
taxId:
- $ref: "#/components/schemas/TaxId"
+ $ref: "#/components/schemas/TaxIdResponse"
address:
$ref: "#/components/schemas/Address"
required:
@@ -1905,16 +2034,15 @@ 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.
- example: "BRL"
+ $ref: "#/components/schemas/Asset"
currentState:
$ref: "#/components/schemas/InstructionState"
address:
@@ -1923,6 +2051,7 @@ components:
type: string
format: date-time
example: "2026-04-14T10:30:00Z"
+ readOnly: true
required:
- id
- asset
@@ -1932,6 +2061,9 @@ components:
responses:
UnauthorizedError:
description: Missing or invalid authentication token.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -2073,6 +2205,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 +2222,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 +2246,8 @@ paths:
firstName: "John"
lastName: "Doe"
taxId:
+ value: "52998224725"
type: "CPF"
- number: "52998224725"
dateOfBirth: "1990-01-15"
address:
addressLine1: "Rua Augusta, 500"
@@ -2144,6 +2275,9 @@ paths:
description: >
Validation error: missing or malformed fields, inline beneficiary not supported, or
inline instruction not supported.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -2199,6 +2333,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 +2351,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 +2363,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 +2429,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 +2446,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 +2475,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 +2511,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 +2525,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 +2537,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 +2591,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 +2608,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 +2641,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 +2677,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 +2691,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 +2703,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 +2776,9 @@ paths:
responses:
"201":
description: Quote created.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -2625,13 +2793,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 +2814,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 +2828,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 +2898,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:
@@ -2753,13 +2923,16 @@ 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"
responses:
"200":
description: Operation details.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -2768,6 +2941,9 @@ paths:
$ref: "#/components/responses/UnauthorizedError"
"404":
description: Operation not found.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -2783,49 +2959,33 @@ 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:
- 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":
description: Invalid filter parameter.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -2880,8 +3040,8 @@ paths:
firstName: "João"
lastName: "Silva"
taxId:
+ value: "52998224725"
type: "CPF"
- number: "52998224725"
dateOfBirth: "1985-03-15"
address:
addressLine1: "Av. Paulista, 1000"
@@ -2904,8 +3064,8 @@ paths:
legalName: "Acme Importação Ltda"
tradeName: "Acme"
taxId:
+ value: "27922482000193"
type: "CNPJ"
- number: "27922482000193"
address:
addressLine1: "Rua da Consolação, 222"
city: "São Paulo"
@@ -2927,8 +3087,8 @@ paths:
firstName: "Jane"
lastName: "Doe"
taxId:
+ value: "11144477735"
type: "CPF"
- number: "11144477735"
dateOfBirth: "1990-07-22"
address:
addressLine1: "123 Main St"
@@ -2944,6 +3104,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 +3115,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,23 +3154,19 @@ 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:
- 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"
@@ -3021,6 +3183,9 @@ paths:
responses:
"200":
description: Beneficiary details.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -3029,6 +3194,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 +3216,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 +3271,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 +3282,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 +3294,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 +3328,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..2cdc7b8 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,10 +142,10 @@ 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
+ - ACCOUNT_ASSET_ACTIVATED
+ - BENEFICIARY_PAYMENT_INSTRUCTION_CREATED
+ - BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED
+ - BENEFICIARY_PAYMENT_INSTRUCTION_REJECTED
- OPERATION_REQUESTED
example: OPERATION_REQUESTED
ExecutionLogStatus:
@@ -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_PAYMENT_INSTRUCTION_APPROVED
+ - BENEFICIARY_PAYMENT_INSTRUCTION_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,27 +455,21 @@ 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
- 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.
example: 2
required:
- value
- - decimalValue
- asset
- decimals
AddressEvent:
@@ -467,6 +510,7 @@ components:
type: string
format: uuid
example: "a1b2c3d4-5e6f-7890-abcd-ef1234567890"
+ readOnly: true
owner:
type: string
description: Registered account owner name.
@@ -483,9 +527,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 +542,7 @@ components:
type: string
format: uuid
example: "d4e5f6a7-b8c9-0123-defa-2345678901bc"
+ readOnly: true
spotRate:
$ref: "#/components/schemas/SpotRateEvent"
required:
@@ -540,14 +585,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 +825,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 +862,7 @@ components:
type: string
format: uuid
example: "c3d4e5f6-a7b8-9012-cdef-1234567890ab"
+ readOnly: true
address:
$ref: "#/components/schemas/FinancialAddressEvent"
required:
@@ -830,6 +878,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 +973,7 @@ components:
id:
type: string
description: Rail-side transaction identifier.
+ readOnly: true
direction:
$ref: "#/components/schemas/Direction"
amount:
@@ -954,10 +1004,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 +1017,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_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
@@ -979,6 +1030,7 @@ components:
id:
type: string
format: uuid
+ readOnly: true
customerId:
type: string
format: uuid
@@ -992,6 +1044,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 +1054,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 +1091,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 +1142,30 @@ paths:
resources:
- name: BENEFICIARY
events:
- - BENEFICIARY_INSTRUCTION_APPROVED
- - BENEFICIARY_INSTRUCTION_REJECTED
+ - BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED
+ - BENEFICIARY_PAYMENT_INSTRUCTION_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 +1178,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 +1203,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 +1250,7 @@ paths:
includeAll: true
- name: BENEFICIARY
events:
- - BENEFICIARY_INSTRUCTION_APPROVED
+ - BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED
disable-retry:
summary: Stop retrying failed deliveries
value:
@@ -1179,16 +1258,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 +1288,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 +1317,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 +1353,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 +1379,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 +1389,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 +1420,9 @@ paths:
responses:
"200":
description: Resource catalog.
+ headers:
+ X-Request-Id:
+ $ref: "#/components/headers/RequestId"
content:
application/json:
schema:
@@ -1308,7 +1431,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.
@@ -1323,9 +1446,9 @@ webhooks:
responses:
"200":
$ref: "#/components/responses/WebhookAck"
- beneficiary.instruction.created:
+ 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
@@ -1344,9 +1467,9 @@ webhooks:
responses:
"200":
$ref: "#/components/responses/WebhookAck"
- beneficiary.instruction.approved:
+ 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
@@ -1364,9 +1487,9 @@ webhooks:
responses:
"200":
$ref: "#/components/responses/WebhookAck"
- beneficiary.instruction.rejected:
+ 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
@@ -1384,8 +1507,7 @@ webhooks:
responses:
"200":
$ref: "#/components/responses/WebhookAck"
-
- operation.requested:
+ OPERATION_REQUESTED:
post:
summary: Operation requested
description: Fires when a payment operation (deposit, withdrawal, swap) is created.
@@ -1399,4 +1521,4 @@ webhooks:
$ref: "#/components/schemas/OperationEvent"
responses:
"200":
- $ref: "#/components/responses/WebhookAck"
+ $ref: "#/components/responses/WebhookAck"
\ 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..be575d9 100644
--- a/guides/principles/datetime.mdx
+++ b/guides/principles/datetime.mdx
@@ -15,11 +15,11 @@ 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 |
| --- | --- | --- |
-| `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` |
@@ -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..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}` 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..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}` 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..31a5cec 100644
--- a/journeys/withdrawal.mdx
+++ b/journeys/withdrawal.mdx
@@ -29,13 +29,18 @@ 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"
+ },
"dateOfBirth": "1990-01-15"
},
+ "relationshipType": "THIRD_PARTY",
"paymentInstruction": {
"type": "PIX",
- "pixKeyType": "CPF",
- "pixKey": "12345678901"
+ "asset": "BRL",
+ "dictKeyType": "CPF",
+ "dictKey": "12345678901"
}
}'
```
@@ -90,7 +95,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..4d7f626 100644
--- a/webhooks/overview.mdx
+++ b/webhooks/overview.mdx
@@ -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..4921f65 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_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_INSTRUCTION_APPROVED"] }
+ { "name": "BENEFICIARY", "events": ["BENEFICIARY_PAYMENT_INSTRUCTION_APPROVED"] }
]
}'
```