# Create an account Source: https://docs.tracefinance.com/api-reference/fx-account/accounts/create-account apis/fx-account/openapi.yml POST /v1/accounts Creates a new multi-currency account for the authenticated customer. # Get an account Source: https://docs.tracefinance.com/api-reference/fx-account/accounts/get-account apis/fx-account/openapi.yml GET /v1/accounts/{accountId} Retrieves the details of an account by its ID. # List accounts Source: https://docs.tracefinance.com/api-reference/fx-account/accounts/list-accounts apis/fx-account/openapi.yml GET /v1/accounts Lists all accounts for the authenticated customer with cursor-based pagination. # Submit account for review Source: https://docs.tracefinance.com/api-reference/fx-account/accounts/submit-account-for-review apis/fx-account/openapi.yml POST /v1/accounts/{accountId}/review Submits an account for compliance review after all requirements are fulfilled. # Update an account Source: https://docs.tracefinance.com/api-reference/fx-account/accounts/update-account apis/fx-account/openapi.yml PATCH /v1/accounts/{accountId} Replace the owner profile and asset selection for an account that has not been submitted for review yet. # Get account balance Source: https://docs.tracefinance.com/api-reference/fx-account/balances/get-balance apis/fx-account/openapi.yml GET /v1/accounts/{accountId}/balances Retrieves the multi-currency balance for the given account, grouped into fiat and crypto arrays. # Add a beneficial owner Source: https://docs.tracefinance.com/api-reference/fx-account/beneficial-owners/add-ubo apis/fx-account/openapi.yml POST /v1/accounts/{accountId}/ubos Adds a beneficial owner (UBO) to a company-owned account. # Get a beneficial owner Source: https://docs.tracefinance.com/api-reference/fx-account/beneficial-owners/get-ubo apis/fx-account/openapi.yml GET /v1/accounts/{accountId}/ubos/{uboId} Retrieves a specific beneficial owner by ID. # List beneficial owners Source: https://docs.tracefinance.com/api-reference/fx-account/beneficial-owners/list-ubos apis/fx-account/openapi.yml GET /v1/accounts/{accountId}/ubos Lists all beneficial owners for an account. # Remove a beneficial owner Source: https://docs.tracefinance.com/api-reference/fx-account/beneficial-owners/remove-ubo apis/fx-account/openapi.yml DELETE /v1/accounts/{accountId}/ubos/{uboId} Removes a beneficial owner from an account. # Update a beneficial owner Source: https://docs.tracefinance.com/api-reference/fx-account/beneficial-owners/update-ubo apis/fx-account/openapi.yml PATCH /v1/accounts/{accountId}/ubos/{uboId} Updates a beneficial owner using patch semantics. Only provided fields are changed. # Upload a document Source: https://docs.tracefinance.com/api-reference/fx-account/documents/upload-document apis/fx-account/openapi.yml POST /v1/accounts/{accountId}/documents Uploads a document to fulfill an account or beneficial owner requirement. # List funding instructions Source: https://docs.tracefinance.com/api-reference/fx-account/funding-instructions/list-funding-instructions apis/fx-account/openapi.yml GET /v1/accounts/{accountId}/fundingInstructions Retrieves funding instructions for depositing into an account across all available payment rails. # Add a payment instruction Source: https://docs.tracefinance.com/api-reference/fx-payment/beneficiaries/add-payment-instruction apis/fx-payment/openapi.yml POST /v1/beneficiaries/{beneficiaryId}/payment-instructions Adds a payment instruction to an existing beneficiary. # Create a beneficiary Source: https://docs.tracefinance.com/api-reference/fx-payment/beneficiaries/create-beneficiary apis/fx-payment/openapi.yml POST /v1/beneficiaries Registers a new beneficiary for withdrawals. # Delete a beneficiary Source: https://docs.tracefinance.com/api-reference/fx-payment/beneficiaries/delete-beneficiary apis/fx-payment/openapi.yml DELETE /v1/beneficiaries/{beneficiaryId} Removes a beneficiary and all its payment instructions. # Get a beneficiary Source: https://docs.tracefinance.com/api-reference/fx-payment/beneficiaries/get-beneficiary apis/fx-payment/openapi.yml GET /v1/beneficiaries/{beneficiaryId} Retrieves the details of a beneficiary by its ID. # List beneficiaries Source: https://docs.tracefinance.com/api-reference/fx-payment/beneficiaries/list-beneficiaries apis/fx-payment/openapi.yml GET /v1/beneficiaries Lists all beneficiaries for the authenticated customer. # Remove a payment instruction Source: https://docs.tracefinance.com/api-reference/fx-payment/beneficiaries/remove-payment-instruction apis/fx-payment/openapi.yml DELETE /v1/beneficiaries/{beneficiaryId}/payment-instructions/{paymentInstructionId} Removes a payment instruction from a beneficiary. # Upload a document Source: https://docs.tracefinance.com/api-reference/fx-payment/beneficiaries/submit-beneficiary-document apis/fx-payment/openapi.yml POST /v1/beneficiaries/{beneficiaryId}/documents Uploads a compliance document, such as a custody attestation, for a beneficiary's payment instruction. # Create a deposit Source: https://docs.tracefinance.com/api-reference/fx-payment/operations/create-deposit apis/fx-payment/openapi.yml POST /v1/operations/deposits Creates a deposit and returns the funding instruction the customer should use to send money in. # Create a swap Source: https://docs.tracefinance.com/api-reference/fx-payment/operations/create-swap apis/fx-payment/openapi.yml POST /v1/operations/swaps Converts funds between assets within a multi-currency account using a locked quote. # Create a transfer Source: https://docs.tracefinance.com/api-reference/fx-payment/operations/create-transfer apis/fx-payment/openapi.yml POST /v1/operations/transfers Moves funds between two accounts of the same customer using a locked quote. # Create a withdrawal Source: https://docs.tracefinance.com/api-reference/fx-payment/operations/create-withdrawal apis/fx-payment/openapi.yml POST /v1/operations/withdrawals Creates a new withdrawal to a saved beneficiary. # Get an operation Source: https://docs.tracefinance.com/api-reference/fx-payment/operations/get-operation apis/fx-payment/openapi.yml GET /v1/operations/{operationId} Retrieves the details of an operation by its ID. # List operations Source: https://docs.tracefinance.com/api-reference/fx-payment/operations/list-operations apis/fx-payment/openapi.yml GET /v1/operations Lists all operations for the authenticated customer. # Create a quote Source: https://docs.tracefinance.com/api-reference/fx-payment/operations/quotes/create-quote apis/fx-payment/openapi.yml POST /v1/quotes Locks an FX rate for a short window. Required by every operation creation endpoint. # Refund a deposit Source: https://docs.tracefinance.com/api-reference/fx-payment/operations/refund-deposit apis/fx-payment/openapi.yml POST /v1/operations/{operationId}/refund Refunds a completed deposit in full, returning the funds to the original payer. # Get a report Source: https://docs.tracefinance.com/api-reference/fx-payment/reports/get-report apis/fx-payment/openapi.yml GET /v1/reports Aggregated view of operations over a time window, partitioned by asset. # Asset activated Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/account/account-asset-activated apis/fx-webhook/openapi.yml webhook ACCOUNT_ASSET_ACTIVATED Fires when an account asset finishes onboarding and funding instructions are available. # Asset failed Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/account/account-asset-failed apis/fx-webhook/openapi.yml webhook ACCOUNT_ASSET_FAILED Fires when an account asset's onboarding fails permanently. # Account created Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/account/account-created apis/fx-webhook/openapi.yml webhook ACCOUNT_CREATED Fires when a new account is created and onboarding starts. # Action required Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/account/action-required apis/fx-webhook/openapi.yml webhook ACCOUNT_ACTION_REQUIRED Fires when a compliance analysis rejects KYC/KYB data and the account is reverted from REVIEWING back to ACTION_REQUIRED. # Instruction action required Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-action-required apis/fx-webhook/openapi.yml webhook BENEFICIARY_INSTRUCTION_ACTION_REQUIRED Fires when compliance asks you for another document on a payment instruction. The instruction returns to review once you upload it. # Instruction approved Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-approved apis/fx-webhook/openapi.yml webhook BENEFICIARY_INSTRUCTION_APPROVED Fires when a payment instruction on a beneficiary is approved. Carries the affected instruction plus beneficiary context. # Instruction created Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created apis/fx-webhook/openapi.yml webhook BENEFICIARY_INSTRUCTION_CREATED Fires when a payment instruction is added to a beneficiary; the new instruction starts in PENDING_REVIEW. # Instruction rejected Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected apis/fx-webhook/openapi.yml webhook BENEFICIARY_INSTRUCTION_REJECTED Fires when a payment instruction on a beneficiary is rejected. Carries the affected instruction plus beneficiary context. # Action required Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/operation/operation-action-required apis/fx-webhook/openapi.yml webhook OPERATION_ACTION_REQUIRED Fires when a payment operation pauses waiting on an action from you before it can proceed. # Completed Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/operation/operation-completed apis/fx-webhook/openapi.yml webhook OPERATION_COMPLETED Fires when a payment operation reaches its terminal success state. # Failed Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/operation/operation-failed apis/fx-webhook/openapi.yml webhook OPERATION_FAILED Fires when a payment operation reaches its terminal failure state. # Requested Source: https://docs.tracefinance.com/api-reference/fx-webhook/events/operation/operation-requested apis/fx-webhook/openapi.yml webhook OPERATION_REQUESTED Fires when a payment operation (deposit, withdrawal, swap) is created. # Create a subscription Source: https://docs.tracefinance.com/api-reference/fx-webhook/subscriptions/create-subscription apis/fx-webhook/openapi.yml POST /v1/subscriptions Register a webhook subscription for a resource. # Delete a subscription Source: https://docs.tracefinance.com/api-reference/fx-webhook/subscriptions/delete-subscription apis/fx-webhook/openapi.yml DELETE /v1/subscriptions/{subscriptionId} Permanently remove a webhook subscription. # Get an execution log Source: https://docs.tracefinance.com/api-reference/fx-webhook/subscriptions/get-execution-log apis/fx-webhook/openapi.yml GET /v1/subscriptions/{subscriptionId}/executionLogs/{executionLogId} Retrieve a single webhook delivery attempt. # Get a subscription Source: https://docs.tracefinance.com/api-reference/fx-webhook/subscriptions/get-subscription apis/fx-webhook/openapi.yml GET /v1/subscriptions/{subscriptionId} Retrieve a single webhook subscription by ID. # List execution logs Source: https://docs.tracefinance.com/api-reference/fx-webhook/subscriptions/list-execution-logs apis/fx-webhook/openapi.yml GET /v1/subscriptions/{subscriptionId}/executionLogs List delivery attempts for a subscription. # List subscriptions Source: https://docs.tracefinance.com/api-reference/fx-webhook/subscriptions/list-subscriptions apis/fx-webhook/openapi.yml GET /v1/subscriptions List webhook subscriptions for the authenticated customer. # Resend a delivery Source: https://docs.tracefinance.com/api-reference/fx-webhook/subscriptions/resend-delivery apis/fx-webhook/openapi.yml POST /v1/subscriptions/{subscriptionId}/executionLogs/{executionLogId}/resend Re-send a previously failed webhook delivery. # Update a subscription Source: https://docs.tracefinance.com/api-reference/fx-webhook/subscriptions/update-subscription apis/fx-webhook/openapi.yml PATCH /v1/subscriptions/{subscriptionId} Update the URL, event types, description, or status of a subscription. # Authentication Source: https://docs.tracefinance.com/guides/authentication Obtain and use JWT tokens to authenticate requests to the Trace Finance API. ## Overview Every request to the Trace Finance API must include a valid JSON Web Token (JWT) in the `Authorization` header. During onboarding you receive a **client ID** and **client secret**. Use these to obtain an access token from the Trace Finance authentication service. ## Details ### Obtaining a token Request an access token from the token endpoint: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/oauth/client/token \ --header 'Content-Type: application/json' \ --data '{ "clientId": "YOUR_CLIENT_ID", "clientSecret": "YOUR_CLIENT_SECRET" }' ``` A successful response includes the token and its lifetime: ```json theme={"theme":"tokyo-night"} { "accessToken": "eyJhbGciOiJSUzI1NiIs...", "tokenType": "Bearer", "expiresIn": 82800 } ``` ### Using the token Include the token in the `Authorization` header of every request: ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/accounts \ --header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIs...' ``` The token contains your customer identity — no separate customer ID header is needed. ### Token lifecycle Tokens are valid for **23 hours** (82,800 seconds). Follow these best practices: 1. **Store securely** — keep the token in memory after obtaining it. 2. **Check before use** — inspect the `exp` claim in the JWT payload to confirm it has not expired. 3. **Rotate proactively** — request a new token before the current one expires rather than waiting for a `401` response. Do not request a new token for every API call. Reuse tokens until they expire. ## Related * [Environments](/guides/environments) — sandbox and production base URLs * [Quickstart](/quickstart) — make your first API call # Beneficiaries Source: https://docs.tracefinance.com/guides/compliance/beneficiaries Sanctions and Travel Rule screening Trace Finance runs on payout recipients, and what the customer keeps responsibility for. ## Overview Beneficiaries are the end-users your platform pays out to. Each payment instruction registered on a beneficiary is screened before it can be used in a withdrawal: a Travel Rule and sanctions check on the recipient and the payment instrument. Trace Finance does not onboard the beneficiary as a customer — the screening is a per-instruction control on outbound transfers. For step-by-step instructions on registering a beneficiary, see the [Register a beneficiary](/journeys/register-beneficiary) journey. ## Details ### What gets screened When a payment instruction is created, Trace Finance runs an automated screening tuned to the rail. * **Identity screening** on the entity's tax ID, full name (or legal name for companies), and address country against sanctions and PEP lists. * **Payment-instrument verification** matched to the rail: * **PIX** — the dict key must resolve in the PIX directory before the instruction is accepted as approved. * **Crypto** — the wallet address is screened against blocklists and risk indicators, and the Travel Rule data required for VASP-to-VASP transfers is captured. A custody attestation document is required for the payment instruction — attach it inline when the beneficiary is created, or afterwards with [Upload a document](/api-reference/fx-payment/beneficiaries/submit-beneficiary-document). The first payment instruction on a new beneficiary triggers the full screening; subsequent instructions on the same beneficiary go through a reduced check focused on the new payment instrument. ### What stays on your side Trace Finance screens the data you submit — it does not verify the underlying end-user. You are expected to: * Verify the identity of every end-user before they receive payouts through Trace Finance. * Maintain your own end-user identity files (identity documents, address proof, source-of-funds where applicable) in line with the regulators that apply to your business. * Ensure the entity data submitted per beneficiary matches what you collected — Travel Rule transmission is only meaningful when the originator and beneficiary data are accurate. * Apply your own internal limits and risk-tiers. Trace Finance's screening is the rail-side gate, not a substitute for your platform's customer-due-diligence program. If your business operates under a regulated regime that requires Trace Finance to participate in the customer review (for example, certain BaaS arrangements), reach out to Trace Finance's compliance team — those arrangements are negotiated separately. ## Related * [Register a beneficiary](/journeys/register-beneficiary) — step-by-step procedure for submitting beneficiaries and payment instructions. * [Testing in sandbox](/guides/testing-in-sandbox) — magic values to simulate rejection and pending outcomes during homologation. * [Withdraw](/journeys/withdrawal) — end-to-end payout flow, including where this screening sits in the lifecycle. # BRL named accounts Source: https://docs.tracefinance.com/guides/compliance/brl-named-accounts KYC and KYB requirements for opening BRL named accounts. ## Overview Named accounts inherit Trace Finance's compliance baseline and add segment-specific information and documents on top. The full document list for the current account is returned in `requirements.currentlyDue` on account creation. * **KYB (Know Your Business)** applies to company-owned accounts (`owner.type = COMPANY`). * **KYC (Know Your Customer)** applies to individually-owned accounts (`owner.type = INDIVIDUAL`) and to every beneficial owner (UBO) registered on a company-owned account. Information is submitted as structured fields on [`POST /v1/accounts`](/api-reference/fx-account/accounts/create-account) and [`POST /v1/accounts/{accountId}/ubos`](/api-reference/fx-account/beneficial-owners/add-ubo); supporting documents are uploaded against [`POST /v1/accounts/{accountId}/documents`](/api-reference/fx-account/documents/upload-document) once the account exists. For Trace Finance-issued accounts, see [BRL virtual accounts](/guides/compliance/brl-virtual-accounts). ## Details ### Business Applies to companies opening a named account. #### Common information and documents The baseline below mirrors the [BRL virtual accounts](/guides/compliance/brl-virtual-accounts#common-information-and-documents) baseline. Submitted on the create-account request: | Field | Format | Notes | | ------------------------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `owner.legalName` | string | Registered legal name on the company's incorporation document. | | `owner.identificationDocument.value` | string | CNPJ for Brazilian companies; foreign tax identifier for non-residents. | | `owner.identificationDocument.type` | enum | `CNPJ` for Brazilian companies; the corresponding type for non-residents. Named accounts require a tax-id type. | | `owner.industry` | enum | Industry classification. See the [create-account schema](/api-reference/fx-account/accounts/create-account) for the full list. | | `owner.incorporateDate` | `yyyy-MM-dd` | Optional. Date of incorporation. | | `owner.isStartup` | boolean | Optional. `true` when the customer is an early-stage company. | | `owner.address` | object | Full registered address (line 1, city, state, country, postal code). | Each beneficial owner (UBO) is registered via [`POST /v1/accounts/{accountId}/ubos`](/api-reference/fx-account/beneficial-owners/add-ubo): | Field | Format | Notes | | -------------------------------------------------------------- | ------------- | ----------------------------------------------------------------------------------- | | `name` | string | Full legal name. | | `identificationDocument.value` / `identificationDocument.type` | string / enum | CPF for Brazilian residents; foreign tax ID for non-residents. | | `address` | object | Residential address. | | `phone` | string | Required. Mobile or contact number. | | `email` | string | Required. | | `ownershipPercentage` | number | Optional. Percentage of ownership. | | `isLegalRepresentative` | boolean | Optional, defaults to `false`. `true` if the UBO can sign on behalf of the company. | Documents required for the company: * Articles of association, bylaws, or board-election minutes registered with the Junta Comercial (or apostilled equivalent for non-residents) — `ARTICLES_OF_ASSOCIATION` or `INCORPORATION_ARTICLES`. * Certificate of incorporation or registration — `INCORPORATION_CERT`. * Annual revenue declaration, income statement (DRE), or balance sheet — signed by the accountant and the legal representative — `FINANCIAL_STATEMENT`. * Registry of directors and shareholders — `DIRECTORS_REGISTRY` and `SHAREHOLDER_REGISTRY`. * Dated and signed organizational chart, if the corporate structure has multiple layers — `ORGANIZATIONAL_CHART`. * Articles or financial statements of any corporate shareholder (PJ partner), if applicable — `PJ_PARTNER_DOCUMENTS` and `PJ_PARTNER_FINANCIAL_STATEMENT`. * Power of attorney, when a signatory acts on the company's behalf — `POWER_OF_ATTORNEY` or `SIGNING_AUTHORITY_PROOF`. Documents required for each UBO: * Government-issued photo ID — RG (`ID_CARD`), CNH (`DRIVER_LICENSE`), passport (`PASSPORT`), or RNM (`RESIDENCE_PERMIT`). * Email address and phone number. * Proof of residence issued within the last 90 days — `PROOF_OF_ADDRESS` or `UTILITY_BILL`. #### Additional information and documents The requested set depends on the account's segment. ##### Payment facilitators (PSPs) Additional information: * Number of employees, company website, and annual revenue. * Commercial-officer visit report. Additional documents: * AML/CTF (PLD/FT) policies, manuals, and registration forms — `AML_POLICY`. * Signed KYC form — `KYC_FORM`. * Signed BaaS adhesion form — `BAAS_ADHESION_FORM`. * CDD (customer due diligence) questionnaire signed by the compliance officer — `OTHER` with descriptive `metadata`. * Service-provision agreement and merchants record sheet — `AGREEMENT` and `OTHER` with descriptive `metadata`. * Power of attorney and identity documents for any signatories acting on the company's behalf — `POWER_OF_ATTORNEY` or `SIGNING_AUTHORITY_PROOF`. ##### Non-residents (CNR) Constitutional documents matching the legal form, all duly apostilled: | Legal form | Required constitutional documents | | ----------------------------------- | ------------------------------------------------------------------------------------------------------ | | Corporation (Corp. / Inc.) | Articles of Incorporation, Bylaws, Share Register | | Limited Liability Company (LLC) | Articles of Organization, Operating Agreement, Membership List | | Limited (Ltd) | Memorandum of Association + Certificate of Incorporation, Articles of Association, Register of Members | | Limited Liability Partnership (LLP) | Incorporation Document (UK) or Certificate of LLP (US), LLP Agreement, Register of Members | Submitted as `ARTICLES_OF_ASSOCIATION`, `INCORPORATION_ARTICLES`, `INCORPORATION_CERT`, and `SHAREHOLDER_REGISTRY` as applicable. Additional documents: * Financial statements signed by the accountant and the legal representative — `FINANCIAL_STATEMENT`. * Corporate organizational chart identifying ultimate beneficial owners — `ORGANIZATIONAL_CHART`. * Articles and financial statements of any PJ partner — `PJ_PARTNER_DOCUMENTS` and `PJ_PARTNER_FINANCIAL_STATEMENT`. * AML/CTF policies — `AML_POLICY`. * Operating licenses, where the entity handles third-party funds — `PROOF_OF_REGULATORY_LICENSE`. * KYC form, due-diligence form, and legal-representative registration form — `KYC_FORM` and `OTHER` with descriptive `metadata`. * Director-election minutes, where applicable — `SIGNING_AUTHORITY_PROOF`. * Wolfsberg questionnaire, when the entity is a financial institution — `WOLFSBERG_QUESTIONNAIRE`. * Latest PLD/CFT audit report, where applicable — `OTHER` with descriptive `metadata`. * PLD/CFT area organizational chart, where applicable — `ORGANIZATIONAL_CHART`. * General terms and conditions for FX operations, and the regulation for opening, operating, maintaining, and closing the BRL non-resident deposit account — `AGREEMENT`. Additional UBO documents: * Proof of residence issued within the last 90 days — `PROOF_OF_ADDRESS` or `UTILITY_BILL`. * Proof of authority to sign on behalf of the company. When the foreign representative does not hold a CPF, a valid power of attorney granting authority to a Brazilian representative — `POWER_OF_ATTORNEY` or `SIGNING_AUTHORITY_PROOF`. ##### Startups Submit `owner.isStartup = true` on the create-account request. Additional documents: * Signed KYC form — `KYC_FORM`. * Complete signed financials covering the last 12 months (DRE + balance sheet) or revenue declaration — `FINANCIAL_STATEMENT`. ##### Crypto businesses Additional documents: * Signed KYC form — `KYC_FORM`. * Signed BaaS adhesion form — `BAAS_ADHESION_FORM`. ##### Funds Additional information: * Exchanges and platforms used to operate. * Target audience, fundraising strategy, and marketing approach. Additional documents: * Appointment instrument, when not covered by the corporate document — `INCORPORATION_ARTICLES` or `SIGNING_AUTHORITY_PROOF`. * Complete signed financials for the last 12 months — `FINANCIAL_STATEMENT`. * Evidence of source of funds — `EVIDENCE_OF_SOURCE_OF_FUNDS`. * Crypto exposure questionnaire (CEQ) — `OTHER` with descriptive `metadata`. Additional UBO documents: * Identity document for every ultimate beneficiary holding 5% or more — `ID_CARD`, `PASSPORT`, `DRIVER_LICENSE`, or `RESIDENCE_PERMIT`. * Proof of address issued within the last 3 months for every ultimate beneficiary holding 5% or more — `PROOF_OF_ADDRESS`. ##### Exchange operators Additional documents: * Terms of service — `AGREEMENT`. * Sample national and international partnership agreements — `AGREEMENT`. * Latest internal and external audit reports, where available — `OTHER` with descriptive `metadata`. * Bylaws or internal regulations — `INCORPORATION_ARTICLES`. * Board-election minutes with active mandate (for `S.A.` companies) — `SIGNING_AUTHORITY_PROOF`. * DRE and financial statements — `FINANCIAL_STATEMENT`. * PCI-DSS AoC or SAQ-D, where available — `OTHER` with descriptive `metadata`. * AML/CTF policies, manuals, and registration forms, where available — `AML_POLICY`. Additional UBO documents: * Digitally certified identification copy for the legal representative — `ID_CARD` with `documentSubType = FRONT_SIDE` and `BACK_SIDE`. * Identification copy for the ultimate beneficiary, when classified as high-risk — `ID_CARD` or `PASSPORT`. ### Individuals Applies to natural persons opening a named account. #### Common information and documents The baseline below mirrors the [BRL virtual accounts](/guides/compliance/brl-virtual-accounts#common-information-and-documents-2) baseline. Submitted on the create-account request: | Field | Format | Notes | | ------------------------------------ | ------------ | --------------------------------------------------------------------- | | `owner.firstName` | string | Given name. | | `owner.lastName` | string | Family name. | | `owner.identificationDocument.value` | string | CPF for Brazilian residents; foreign tax identifier otherwise. | | `owner.identificationDocument.type` | enum | `CPF` for Brazilian residents; the corresponding type otherwise. | | `owner.birthDate` | `yyyy-MM-dd` | Optional. Date of birth. | | `owner.address` | object | Full residential address (line 1, city, state, country, postal code). | Additional information collected outside the account record: * Email address and phone number. * Marital status, and the spouse's full name and CPF when married. Documents: * Government-issued photo ID — RG (`ID_CARD`), CNH (`DRIVER_LICENSE`), passport (`PASSPORT`), RNM or MRE (`RESIDENCE_PERMIT`). Some segments require the document to have been issued within the last 10 years. * Proof of residence issued within the last 90 days — `PROOF_OF_ADDRESS` or `UTILITY_BILL`. * Liveness selfie matched to the photo ID — `SELFIE`. #### Additional information and documents Required for individuals with non-standard fiscal residency, higher-risk activity, or where reinforced diligence applies. Additional information: * Tax identification number (NIF or equivalent) in the country of fiscal residence, for non-Brazilian residents. * Employer name, profession, monthly income, and net worth. Additional documents: * Proof of income — most recent income tax return (current year, with filing receipt) in the country of fiscal residence, the last three pay stubs, or equivalent evidence — `EVIDENCE_OF_SOURCE_OF_WEALTH`. * Evidence of source of funds tied to a specific transaction or asset (for example, property record, purchase agreement or final deed, and statement showing receipt of funds when the activity relates to a real-estate disposal) — `EVIDENCE_OF_SOURCE_OF_FUNDS`. * Signed account-opening agreement — `AGREEMENT`. * FATCA form for individuals classified as foreign or with fiscal residence abroad — `OTHER` with descriptive `metadata`. ## Related * [BRL virtual accounts](/guides/compliance/brl-virtual-accounts) — requirements for Trace Finance-issued virtual accounts. * [Open a named account](/journeys/open-named-account) — end-to-end flow that submits owner details, uploads documents, and registers UBOs. * [Create an account](/api-reference/fx-account/accounts/create-account) — request schema for company and individual owners. * [Upload a document](/api-reference/fx-account/documents/upload-document) — endpoint used to submit each required document. * [Add a UBO](/api-reference/fx-account/beneficial-owners/add-ubo) — register a beneficial owner for a company-owned account. # BRL virtual accounts Source: https://docs.tracefinance.com/guides/compliance/brl-virtual-accounts KYC and KYB requirements for opening Trace Finance-issued BRL virtual accounts. ## Overview Virtual accounts are issued directly by Trace Finance and used by customers who run their own treasury, intercompany flows, or proprietary commerce on Trace Finance. Verification follows Trace Finance's compliance baseline only. * **KYB (Know Your Business)** applies to company-owned accounts (`owner.type = COMPANY`). * **KYC (Know Your Customer)** applies to individually-owned accounts (`owner.type = INDIVIDUAL`) and to every beneficial owner (UBO) registered on a company-owned account. Information is submitted as structured fields on [`POST /v1/accounts`](/api-reference/fx-account/accounts/create-account) and [`POST /v1/accounts/{accountId}/ubos`](/api-reference/fx-account/beneficial-owners/add-ubo); supporting documents are uploaded against [`POST /v1/accounts/{accountId}/documents`](/api-reference/fx-account/documents/upload-document) once the account exists. The full document list for the current account is returned in `requirements.currentlyDue` on account creation. For named accounts, see [BRL named accounts](/guides/compliance/brl-named-accounts). ## Details ### Business Applies to companies registering a virtual account as the direct customer of Trace Finance. The same baseline covers Brazilian entities and non-residents. #### Common information and documents Submitted on the create-account request: | Field | Format | Notes | | -------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------ | | `owner.legalName` | string | Registered legal name on the company's incorporation document. | | `owner.identificationDocument.value` | string | CNPJ for Brazilian companies; foreign tax identifier for non-residents. For a `PASSPORT`, the passport number. | | `owner.identificationDocument.type` | enum | `CNPJ` for Brazilian companies, the corresponding type for non-residents, or `PASSPORT`. | | `owner.identificationDocument.country` | enum | Required when `type` is `PASSPORT`; omit for tax-id types, which imply their country. | | `owner.industry` | enum | Industry classification. See the [create-account schema](/api-reference/fx-account/accounts/create-account) for the full list. | | `owner.incorporateDate` | `yyyy-MM-dd` | Optional. Date of incorporation. | | `owner.isStartup` | boolean | Optional. `true` when the customer is an early-stage company. | | `owner.address` | object | Full registered address (line 1, city, state, country, postal code). | Each beneficial owner (UBO) is registered via [`POST /v1/accounts/{accountId}/ubos`](/api-reference/fx-account/beneficial-owners/add-ubo): | Field | Format | Notes | | -------------------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------- | | `name` | string | Full legal name. | | `identificationDocument.value` / `identificationDocument.type` | string / enum | CPF for Brazilian residents, foreign tax ID for non-residents, or a `PASSPORT` (supply `country`). | | `address` | object | Residential address. | | `phone` | string | Required. Mobile or contact number. | | `email` | string | Required. | | `ownershipPercentage` | number | Optional. Percentage of ownership. | | `isLegalRepresentative` | boolean | Optional, defaults to `false`. `true` if the UBO can sign on behalf of the company. | Documents required for the company. Each bullet shows the canonical `documentType` value for [`POST /v1/accounts/{accountId}/documents`](/api-reference/fx-account/documents/upload-document): * Articles of association, bylaws, or equivalent constitutional document, registered with the relevant commercial registry (Junta Comercial in Brazil; companies house or equivalent for non-residents, duly apostilled) — `ARTICLES_OF_ASSOCIATION` or `INCORPORATION_ARTICLES`. * Certificate of incorporation or registration — `INCORPORATION_CERT`. * Annual revenue declaration, income statement (DRE), or balance sheet — signed by the accountant and the legal representative — `FINANCIAL_STATEMENT`. * Registry of directors and shareholders — `DIRECTORS_REGISTRY` and `SHAREHOLDER_REGISTRY`. * Dated and signed organizational chart, if the corporate structure has multiple layers — `ORGANIZATIONAL_CHART`. * Articles or financial statements of any corporate shareholder (PJ partner), if applicable — `PJ_PARTNER_DOCUMENTS` and `PJ_PARTNER_FINANCIAL_STATEMENT`. * Power of attorney, when a signatory acts on the company's behalf — `POWER_OF_ATTORNEY` or `SIGNING_AUTHORITY_PROOF`. Documents required for each UBO: * Government-issued photo ID — RG (`ID_CARD`), CNH (`DRIVER_LICENSE`), passport (`PASSPORT`), or RNM (`RESIDENCE_PERMIT`). * Proof of residence issued within the last 90 days — `PROOF_OF_ADDRESS` or `UTILITY_BILL`. #### Additional information and documents The items below apply only when the account owner is itself a regulated payment entity or otherwise handles third-party funds — payment service providers and facilitators, banking-as-a-service providers, virtual-asset service providers (exchanges, custodians, brokers), foreign-exchange correspondents, and other financial institutions. They do not apply to ordinary corporates onboarding for their own treasury, intercompany flows, or proprietary commerce. Additional information (collected outside the account record today): * Public website or primary digital channel. * Declared economic purpose of the account (proprietary treasury, intercompany flow, payment intermediation, etc.). * Expected monthly volume, with currency. * Average ticket size, with currency. Additional documents: * AML/CTF (PLD/FT) policies and procedures, including the customer's onboarding process for its own users, the process for monitoring suspicious user transactions, and the designated person in charge of AML-FT and Compliance — `AML_POLICY`. * Evidence of the KYC and transaction or wallet monitoring systems in use — `KYC_MONITORING_EVIDENCE`. * Operating or regulatory license issued by the competent authority of the jurisdiction in which the customer operates — `PROOF_OF_REGULATORY_LICENSE`. * Evidence of source of funds and source of wealth, where the activity profile requires it — `EVIDENCE_OF_SOURCE_OF_FUNDS` and `EVIDENCE_OF_SOURCE_OF_WEALTH`. ### Individuals Applies to natural persons opening a Trace Finance virtual account in their own name. #### Common information and documents Submitted on the create-account request: | Field | Format | Notes | | -------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------- | | `owner.firstName` | string | Given name. | | `owner.lastName` | string | Family name. | | `owner.identificationDocument.value` | string | CPF for Brazilian residents; foreign tax identifier otherwise. For a `PASSPORT`, the passport number. | | `owner.identificationDocument.type` | enum | `CPF` for Brazilian residents, the corresponding type otherwise, or `PASSPORT`. | | `owner.identificationDocument.country` | enum | Required when `type` is `PASSPORT`; omit for tax-id types, which imply their country. | | `owner.birthDate` | `yyyy-MM-dd` | Optional. Date of birth. | | `owner.address` | object | Full residential address (line 1, city, state, country, postal code). | Documents: * Government-issued photo ID — RG (`ID_CARD`), CNH (`DRIVER_LICENSE`), passport (`PASSPORT`), RNM (`RESIDENCE_PERMIT`), or MRE (`ID_CARD`). * Proof of residence issued within the last 90 days — `PROOF_OF_ADDRESS` or `UTILITY_BILL`. * Liveness selfie matched to the photo ID — `SELFIE`. #### Additional information and documents Required for individuals with non-standard fiscal residency, higher-risk activity, or where the customer's compliance team requests reinforced diligence. Collected outside the account record unless an explicit `documentType` is listed. Additional information: * Marital status, and the spouse's full name and CPF when married. * Tax identification number (NIF or equivalent) in the country of fiscal residence, for non-Brazilian residents. * Profession, employer name, monthly income, and net worth. Additional documents: * Proof of income — required only when the individual is expected to process more than USD 10,000 per month. Most recent income tax return, the last three pay stubs, or equivalent evidence — `EVIDENCE_OF_SOURCE_OF_WEALTH`. * FATCA form for individuals classified as foreign or with fiscal residence abroad — `OTHER` with descriptive `metadata`. ## Related * [BRL named accounts](/guides/compliance/brl-named-accounts) — requirements for named accounts. * [Open a virtual account](/journeys/open-virtual-account) — end-to-end flow that submits owner details, uploads documents, and registers UBOs. * [Create an account](/api-reference/fx-account/accounts/create-account) — request schema for company and individual owners. * [Upload a document](/api-reference/fx-account/documents/upload-document) — endpoint used to submit each required document. * [Add a UBO](/api-reference/fx-account/beneficial-owners/add-ubo) — register a beneficial owner for a company-owned account. # Crypto assets and networks Source: https://docs.tracefinance.com/guides/crypto-assets Stablecoins Trace Finance supports in production, with the network and contract address that identify each one. ## Overview Trace Finance settles crypto deposits, withdrawals, and swaps in a fixed set of stablecoins. Each asset is identified by three things together: its `symbol` (ticker), the `network` it settles on, and the token `contractAddress` on that network. The same symbol on two networks is a different token at a different address. The addresses below are **production mainnet** contracts. The `network` value determines which chain a transfer settles on — sending funds on the wrong network, or to an address from a different network, results in permanent loss. For sandbox testing, use the testnet tokens in [Testing in sandbox](/guides/testing-in-sandbox) instead. Most stablecoins use 6 decimals, matching the [money format](/guides/principles/money). The one exception below is **USDT on BSC**, which uses 18 decimals — always read `decimals` from the amount object rather than assuming a fixed scale per symbol. Supported networks: `ETHEREUM`, `BASE`, `POLYGON`, `TRON`, `SOLANA`, and `BSC`. Not every asset is available on every network — see each asset below. ## Details ### Tether (`USDT`) | `network` | `contractAddress` | `decimals` | | ---------- | ---------------------------------------------- | ---------- | | `ETHEREUM` | `0xdac17f958d2ee523a2206206994597c13d831ec7` | 6 | | `TRON` | `TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t` | 6 | | `SOLANA` | `Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB` | 6 | | `POLYGON` | `0xc2132d05d31c914a87c6611c10748aeb04b58e8f` | 6 | | `BASE` | `0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2` | 6 | | `BSC` | `0x55d398326f99059ff775485246999027b3197955` | 18 | ### USD Coin (`USDC`) | `network` | `contractAddress` | `decimals` | | ---------- | ---------------------------------------------- | ---------- | | `ETHEREUM` | `0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48` | 6 | | `BASE` | `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` | 6 | | `POLYGON` | `0x3c499c542cef5e3811e1192ce70d8cc03d5c3359` | 6 | | `TRON` | `TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8` | 6 | | `SOLANA` | `EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v` | 6 | ### Euro Coin (`EURC`) | `network` | `contractAddress` | `decimals` | | ---------- | -------------------------------------------- | ---------- | | `ETHEREUM` | `0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c` | 6 | ## Related * [Money and currencies](/guides/principles/money) — how amounts and decimals are represented on the wire * [Testing in sandbox](/guides/testing-in-sandbox) — testnet tokens for homologation * [Environments](/guides/environments) — sandbox and production base URLs # Environments Source: https://docs.tracefinance.com/guides/environments Sandbox and production base URLs, and what differs between them. ## Overview Trace Finance provides two environments. Use sandbox for development and testing, and production for live integrations. The sandbox base URL is `https://api.sandbox.tracefinance.com`. The production base URL is shared with you at go-live, after your integration has been validated in sandbox. ## Details ### Sandbox The sandbox is an isolated environment that replicates production behavior with test data. Use it to build and validate your integration before going live. * No real money is moved. * Test credentials are provided during onboarding. * Data may be reset periodically. ### Production 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. ### Rate limits API requests are subject to rate limits in both environments. See [Errors](/guides/principles/errors) for status codes and retry guidance. ### Availability The platform maintains **99.8% uptime** with the exception of scheduled maintenance or unforeseeable events. Scheduled maintenance is communicated at least **21 business days** in advance through official channels. ## Related * [Authentication](/guides/authentication) — how to obtain tokens for each environment * [Quickstart](/quickstart) — make your first sandbox API call # Date and time Source: https://docs.tracefinance.com/guides/principles/datetime How dates and timestamps are formatted in the API. ## Overview All dates and timestamps in the Trace Finance API use **UTC** in ISO 8601 format. No other timezones are accepted or returned. ## How it works ### Format ```text theme={"theme":"tokyo-night"} yyyy-MM-ddTHH:mm:ss.SSSZ ``` Example: `2026-04-22T20:00:00Z` | Component | Description | Example | | --------- | ------------------------------- | ------- | | `yyyy` | Four-digit year | `2026` | | `MM` | Two-digit month (01–12) | `01` | | `dd` | Two-digit day (01–31) | `15` | | `T` | Date/time separator | `T` | | `HH` | Hours in 24-hour format (00–23) | `14` | | `mm` | Minutes (00–59) | `30` | | `ss` | Seconds (00–59) | `00` | | `.SSS` | Milliseconds (optional) | `.000` | | `Z` | UTC timezone indicator | `Z` | ## Examples A resource creation timestamp: ```json theme={"theme":"tokyo-night"} { "createdAt": "2026-04-22T20:00:00Z" } ``` Filtering by date range: ```bash theme={"theme":"tokyo-night"} ?filters=and(createdAt[gte]=2026-04-01T00:00:00Z,createdAt[lte]=2026-04-30T23:59:59Z) ``` # Errors Source: https://docs.tracefinance.com/guides/principles/errors How errors are structured and how to handle them. ## Overview The Trace Finance API uses standard HTTP status codes and returns structured error responses. Every response includes an `X-Request-Id` header you can reference when contacting support. ## How it works ### Error response structure All errors follow the same shape: ```json theme={"theme":"tokyo-night"} { "code": "INVALID_DATA", "message": "The field 'amount.value' must be a positive integer.", "details": {} } ``` | Field | Type | Description | | --------- | ------ | ----------------------------------------------------- | | `code` | string | Machine-readable error code for programmatic handling | | `message` | string | Human-readable description of what went wrong | | `details` | object | Additional context (may be empty) | ### HTTP status codes | Code | Meaning | When it happens | | ----- | --------------------- | ------------------------------------------ | | `200` | OK | Request succeeded | | `201` | Created | Resource was created | | `204` | No content | Request succeeded with no response body | | `400` | Bad request | Invalid or malformed request data | | `401` | Unauthorized | Missing or invalid authentication token | | `404` | Not found | Resource does not exist | | `408` | Request timeout | Request took too long to process | | `409` | Conflict | Idempotency key conflict or state conflict | | `422` | Unprocessable entity | Valid syntax but business rule violation | | `429` | Too many requests | Rate limit exceeded | | `500` | Internal server error | Unexpected server failure | ### Common error codes | Code | HTTP status | Description | | ---------------------------- | ----------- | ---------------------------------------------------------- | | `INVALID_DATA` | 400 | Request body failed validation | | `REQUIRED` | 400 | Required field is missing or blank | | `INVALID_UUID` | 400 | Field value is not a valid UUID | | `INVALID_ENUM` | 400 | Field value is not a valid enum option | | `INVALID_DATE` | 400 | Field value is not a valid date format | | `INVALID_TYPE` | 400 | Field value does not match the expected type | | `MISSING_REQUIRED_HEADER` | 400 | Required HTTP header is missing | | `INCOMPATIBLE_CURRENCY` | 400 | Currency does not match the expected value | | `INVALID_FILTER_FORMAT` | 400 | Filter query parameter is malformed | | `RESOURCE_NOT_FOUND` | 404 | Requested resource does not exist | | `IDEMPOTENT_ID_CONFLICT` | 409 | Idempotency key was already used with a different request | | `RESOURCE_ALREADY_EXISTS` | 409 | Resource with the given identifier already exists | | `ATTRIBUTE_MISMATCH` | 409 | Field value conflicts with existing data | | `INVALID_MFA_CODE` | 409 | Provided MFA code is incorrect | | `INVALID_STATUS_CHANGE` | 422 | Resource cannot transition to the requested state | | `CANNOT_EXECUTE_ACTION` | 422 | Action is not allowed in the current resource state | | `REQUIRED_ATTRIBUTE_IS_NULL` | 422 | Required attribute is missing or null | | `MFA_NOT_ENABLED` | 422 | Multi-factor authentication is required but not configured | ### Retry guidance | Status code | Should retry? | Strategy | | -------------------------- | ------------- | --------------------------------------------- | | `400`, `401`, `404`, `422` | No | Fix the request before retrying | | `408`, `429` | Yes | Back off and retry after the indicated period | | `409` | Depends | Check if the original request succeeded | | `500` | Yes | Retry with exponential backoff | Include the `X-Request-Id` from the response when opening a support ticket. This helps the team trace your request through the system. ## Examples A validation error: ```json theme={"theme":"tokyo-night"} { "code": "INVALID_DATA", "message": "The field 'amount.value' must be a positive integer.", "details": { "field": "amount.value" } } ``` A resource not found: ```json theme={"theme":"tokyo-night"} { "code": "RESOURCE_NOT_FOUND", "message": "Account with id 'abc-123' was not found.", "details": {} } ``` # Filtering Source: https://docs.tracefinance.com/guides/principles/filtering Refine list results using the filters query parameter. ## Overview List endpoints support a `filters` query parameter that lets you narrow results by field values, ranges, and patterns using LHS Brackets syntax. ## How it works ### Syntax Filters follow the format `field[operator]=value`: ```text theme={"theme":"tokyo-night"} ?filters=field[operator]=value ``` Combine multiple filters with a semicolon (`;`) — groups separated by `;` are implicitly AND-ed. Group conditions with logical operators `and(...)` and `or(...)`, separating conditions inside with commas. Use the string `null` to filter for null values: `field[eq]=null`. ### Operators | Operator | Description | Example | | --------- | ------------------------------------------ | ------------------------------------- | | `eq` | Equals | `id[eq]=abc-123` | | `ne` | Not equals | `status[ne]=CLOSED` | | `gt` | Greater than | `amount.value[gt]=1000` | | `gte` | Greater than or equal | `createdAt[gte]=2025-01-01T00:00:00Z` | | `lt` | Less than | `amount.value[lt]=50000` | | `lte` | Less than or equal | `createdAt[lte]=2025-12-31T23:59:59Z` | | `in` | Matches any value in list (pipe-separated) | `type[in]=CHECKING\|SAVING` | | `nin` | Excludes values in list (pipe-separated) | `status[nin]=CLOSED\|SUSPENDED` | | `like` | Case-insensitive partial match | `name[like]=trace` | | `text` | Full-text search (case-insensitive) | `description[text]=payment` | | `last` | Last element of array equals | `states.status[last]=ACTIVE` | | `last_ne` | Last element of array not equals | `states.status[last_ne]=CLOSED` | ### Logical operators Combine conditions using `and(...)` or `or(...)`: ```text theme={"theme":"tokyo-night"} ?filters=and(amount.value[gt]=1000,amount.value[lt]=50000) ``` ## Examples **Filter by ID:** ```bash theme={"theme":"tokyo-night"} ?filters=id[eq]=399ca839-abd5-4a7d-981b-f187e7777ec8 ``` **Filter by date range:** ```bash theme={"theme":"tokyo-night"} ?filters=and(createdAt[gte]=2025-01-01T00:00:00Z,createdAt[lte]=2025-01-31T23:59:59Z) ``` **Filter by type (multiple values):** ```bash theme={"theme":"tokyo-night"} ?filters=type[in]=CHECKING|SAVING ``` **Filter by current status:** ```bash theme={"theme":"tokyo-night"} ?filters=states.status[last]=ACTIVE ``` **Combined filters:** ```bash theme={"theme":"tokyo-night"} ?filters=states.status[last]=ACTIVE;and(amount.value[gt]=1000,amount.value[lt]=50000) ``` Full request example: ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url 'https://api.sandbox.tracefinance.com/v1/accounts?limit=10&filters=states.status[last]=ACTIVE' \ --header 'Authorization: Bearer ' ``` # Idempotency Source: https://docs.tracefinance.com/guides/principles/idempotency Use the X-Idempotency-Key header to safely retry requests. ## Overview Idempotency ensures that performing the same operation multiple times produces the same result as performing it once. This protects against duplicate operations when requests are retried due to timeouts or network failures. ## How it works ### The X-Idempotency-Key header Mutating endpoints (POST, PUT, PATCH) require the `X-Idempotency-Key` header. This client-generated key lets the API detect and deduplicate repeated requests. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/operations/withdrawals \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000' \ --header 'Content-Type: application/json' \ --data '{ ... }' ``` ### When it is required The `X-Idempotency-Key` header is required on any request that creates or modifies a resource. GET and DELETE requests do not require it. ### Conflict behavior If a request arrives with an idempotency key that was already used, the API returns HTTP `409 Conflict`: ```json theme={"theme":"tokyo-night"} { "code": "IDEMPOTENT_ID_CONFLICT", "message": "A request with this idempotency key has already been processed.", "details": {} } ``` ## Examples Generate a UUID v4 for each unique operation: ```python theme={"theme":"tokyo-night"} import uuid idempotency_key = str(uuid.uuid4()) ``` ```javascript theme={"theme":"tokyo-night"} const idempotencyKey = crypto.randomUUID(); ``` Retrying safely after a timeout: ```bash theme={"theme":"tokyo-night"} # First attempt — times out curl ... --header 'X-Idempotency-Key: 550e8400-...' # Retry with the same key — safe, returns original result curl ... --header 'X-Idempotency-Key: 550e8400-...' ``` Never reuse an idempotency key for a different operation. Each unique operation must have its own key. # Money and currencies Source: https://docs.tracefinance.com/guides/principles/money How monetary amounts are represented in the API. ## Overview Monetary amounts in the Trace Finance 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) and stablecoins (6 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 a response uses the same structure: ```json theme={"theme":"tokyo-night"} { "value": "0.11", "asset": "BRL", "decimals": 2 } ``` | Field | Type | Description | | ---------- | ------- | ------------------------------------------------------------------------------------- | | `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. | ### In request bodies Quote and operation requests take amounts as a **decimal-string scalar** paired with a separate asset field — not the full object: ```json theme={"theme":"tokyo-night"} { "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"` | | `USDT` | 6 | `"100.000000"` | | `USDC` | 6 | `"1.500000"` | ## Examples A deposit of R\$ 1.250,00: ```json theme={"theme":"tokyo-night"} { "amount": { "value": "1250.00", "asset": "BRL", "decimals": 2 } } ``` A withdrawal of 500 USDT: ```json theme={"theme":"tokyo-night"} { "amount": { "value": "500.000000", "asset": "USDT", "decimals": 6 } } ``` # Pagination Source: https://docs.tracefinance.com/guides/principles/pagination Navigate large result sets with cursor-based pagination. ## Overview List endpoints return paginated responses using cursor-based pagination. This approach provides stable results even when data changes between requests. ## How it works ### Query parameters | Parameter | Type | Default | Description | | ----------- | ------- | ------------ | ------------------------------------------- | | `limit` | integer | `10` | Maximum number of items to return per page | | `cursor` | string | — | Cursor returned from a previous response | | `direction` | string | — | Pagination direction: `NEXT` or `PREVIOUS` | | `sortOrder` | string | `DESCENDING` | Sort direction: `ASCENDING` or `DESCENDING` | ### Response structure Every list response includes a `meta` object alongside the `data` array: ```json theme={"theme":"tokyo-night"} { "data": [ { "id": "acc_001", "type": "CHECKING" }, { "id": "acc_002", "type": "SAVING" } ], "meta": { "previousCursor": null, "nextCursor": "eyJpZCI6ImFjY18wMDIifQ", "total": 45 } } ``` | Field | Description | | ---------------- | ------------------------------------------------------------ | | `previousCursor` | Cursor to fetch the previous page. `null` on the first page. | | `nextCursor` | Cursor to fetch the next page. `null` on the last page. | | `total` | Number of items returned in the current page. | ## Examples **First page:** ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url 'https://api.sandbox.tracefinance.com/v1/accounts?limit=10' \ --header 'Authorization: Bearer ' ``` **Next page** — pass the `nextCursor` value as `cursor`: ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url 'https://api.sandbox.tracefinance.com/v1/accounts?limit=10&cursor=eyJpZCI6ImFjY18wMDIifQ' \ --header 'Authorization: Bearer ' ``` **Iterating through all pages:** ```python theme={"theme":"tokyo-night"} cursor = None while True: params = {"limit": 10} if cursor: params["cursor"] = cursor response = client.get("/accounts", params=params) data = response.json() for item in data["data"]: process(item) cursor = data["meta"]["nextCursor"] if cursor is None: break ``` # Versioning Source: https://docs.tracefinance.com/guides/principles/versioning How API versioning works via the X-Trace-Version header. ## Overview The Trace Finance API uses the `X-Trace-Version` header to route requests to a specific API version. This lets you pin your integration to a known version while newer versions are released. ## How it works ### Setting the version Include the `X-Trace-Version` header in your requests: ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/accounts \ --header 'Authorization: Bearer ' \ --header 'X-Trace-Version: 2' ``` If the header is omitted, empty, or contains a non-existent version, the request is routed to the **default version** configured by Trace Finance. Always pin a version in production to avoid unexpected behavior when the default changes. ### Non-breaking changes The following changes do not require a new version. They can be released at any time: * Adding new **optional** fields to responses * Adding new endpoints * Adding new optional query parameters Configure your client to ignore unknown fields in responses. Strict validation may cause failures when optional fields are added. ### Breaking changes The following changes trigger a **new major version**: * Removing or renaming existing fields * Changing a field's data type (e.g. integer to string) * Making an optional field required * Reorganizing the response structure * Changing endpoint behavior, status codes, or error responses * Adding new enum values that affect strict validation ### Deprecation policy When a new version is released, the previous version remains available for a migration period. Deprecation timelines are communicated in advance so you can plan your upgrade. ## Examples Pinned version: ```bash theme={"theme":"tokyo-night"} X-Trace-Version: 2 ``` No version header (uses default): ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/accounts \ --header 'Authorization: Bearer ' ``` # Testing in sandbox Source: https://docs.tracefinance.com/guides/testing-in-sandbox Magic values and triggers the Trace Finance sandbox accepts to simulate compliance and settlement outcomes during homologation. ## Overview The sandbox base URL is `https://api.sandbox.tracefinance.com`. It runs the same state machines as production, so most requests follow the happy path by default: beneficiaries approve, payment instructions clear, and operations settle. To exercise the other branches, the sandbox recognizes a set of deterministic magic values and triggers. Sending one forces a specific compliance or settlement outcome without waiting for a real upstream error, so you can develop and verify each path during homologation. The sections below list every trigger, grouped by the resource it acts on. To build the webhook handler that receives the resulting events, see [Test webhooks in sandbox](/webhooks/test-in-sandbox). ## Beneficiaries A beneficiary you create **auto-approves by default**. Use the triggers below to force other outcomes. ### Force the pending state Send `entity.address.addressLine2` containing `no-auto` (case-sensitive, substring match) to keep the beneficiary in its initial pending state. Use this to validate UI states, polling, and webhook delivery before the decision lands. ```json theme={"theme":"tokyo-night"} "address": { "addressLine1": "Rua Augusta, 500", "addressLine2": "Apto 12 no-auto" } ``` ### Force a rejection Send any of the following values to receive a rejected beneficiary. | Field | Value | Simulated reason | | --------------------------------------------------------- | ------------------------- | ---------------------------- | | `entity.identificationDocument.value` (with `type: CPF`) | `12345678909` | Tax ID owner deceased | | `entity.identificationDocument.value` (with `type: CNPJ`) | `50676745000143` | Company deregistered | | `entity.firstName` + `entity.lastName` | `John` + `Doe` (exact) | Name match on sanctions list | | `entity.address.country` | `KP`, `IR`, `SY`, or `CU` | Sanctioned country | ### Pix payment instructions A Pix instruction either approves (auto-approves in sandbox, behind a real compliance review in production) or rejects after the review settles. Once approved, the customer's withdrawal against the instruction either completes or fails on the rail. The triggers below let you exercise both terminal-failure paths from your integration without waiting for real upstream errors. #### Force a payment-instruction rejection Use this trigger to develop and verify your `BENEFICIARY_INSTRUCTION_REJECTED` handler. Submit a [`POST /v1/beneficiaries`](/api-reference/fx-payment/beneficiaries/create-beneficiary) request with the magic dict key on the Pix payment instruction: | `paymentInstruction.dictKey` | `paymentInstruction.dictKeyType` | Outcome | | ------------------------------ | -------------------------------- | --------------------------------------------------------------------------------------------------- | | `reject@sandbox.trace.finance` | `EMAIL` | The Pix instruction is rejected; `BENEFICIARY_INSTRUCTION_REJECTED` fires shortly after submission. | ```json theme={"theme":"tokyo-night"} "paymentInstruction": { "rail": "PIX_KEY", "asset": "BRL", "dictKeyType": "EMAIL", "dictKey": "reject@sandbox.trace.finance" } ``` Any other dict key follows the normal sandbox resolution path. The endpoint responds `201 Created` with the new instruction in `PENDING_REVIEW`, the same shape as a normal submission. Shortly after, the [`BENEFICIARY_INSTRUCTION_REJECTED`](/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected) webhook arrives with the rejection on the affected instruction: ```json theme={"theme":"tokyo-night"} { ..., "instruction": { ..., "currentState": { "status": "REJECTED", "reason": { "code": "COMPLIANCE_REJECTED", "message": "An unknown error occurred.", ... }, ... }, ... }, ... } ``` A rejected instruction is terminal. To retry, register a new instruction, or a new beneficiary if the entity data was wrong. #### Force a withdrawal failure Use this trigger to develop and verify your `OPERATION_FAILED` handler. Register a beneficiary with any normal dict key so the instruction approves cleanly, then submit a [`POST /v1/operations/withdrawals`](/api-reference/fx-payment/operations/create-withdrawal) request using a quote whose `sourceAmount` cents select the simulated outcome: | `sourceAmount` cents | `currentState.reason.code` | `currentState.reason.message` | | -------------------- | ---------------------------------- | ------------------------------------------------------------------------------------ | | `.04` | `INVALID_ACCOUNT` | "Invalid account." | | `.05` | `CPF_CNPJ_ACCOUNT_HOLDER_MISMATCH` | "CPF/CNPJ of the receiving user is not consistent with the account holder credited." | | `.06` | `CLIENT_ACCOUNT_BALANCE_BLOCKED` | "Client's account balance is blocked." | | `.07` | `INSUFFICIENT_BALANCE` | "Insufficient balance." | | `.08` | `TRANSACTION_LIMIT_EXCEEDED` | "Daily equal transaction limit exceeded." | | `.09` | `PROCESSING_ERROR` | "Transaction processing failed." | Any other cents value settles the withdrawal normally. The trigger reads `sourceAmount` on the outbound withdrawal only, so beneficiary registration is unaffected and an approved beneficiary stays usable across all six triggers. Quote `500.04` BRL → `500.04` BRL (or any same-asset amount whose cents are `.04`), then submit the withdrawal against the approved instruction. The endpoint responds `201 Created` with the operation in `REQUESTED`, the same shape as a normal withdrawal. Shortly after, the [`OPERATION_FAILED`](/api-reference/fx-webhook/events/operation/operation-failed) webhook arrives with the matching reason on `currentState`: ```json theme={"theme":"tokyo-night"} { ..., "sourceAmount": { "value": "500.04", "asset": "BRL", "decimals": 2 }, ..., "currentState": { "status": "FAILED", "reason": { "code": "INVALID_ACCOUNT", "message": "Invalid account.", ... }, ... }, ... } ``` The webhook is terminal; no further events fire for this operation. Cycle through the six magic cents values to exercise six independent rail-error branches of your handler without re-registering beneficiaries. ### Crypto wallet instructions Crypto wallets are screened against a real chain-analysis provider in both sandbox and production. There is no sandbox-only mock layer. To simulate a rejected beneficiary, send a `paymentInstruction.address` that the provider rejects in the real world: for example, an address with an invalid format or checksum, or a publicly known blocklisted address. The same submission against production returns the same rejection. #### Force a custody-attestation document rejection A crypto beneficiary also submits a **custody attestation** document. Independently of wallet screening, you can reject the beneficiary through its document check by uploading the attestation with a filename that contains the lowercase word `reject`, either inline on [`POST /v1/beneficiaries`](/api-reference/fx-payment/beneficiaries/create-beneficiary) or via [`POST /v1/beneficiaries/{beneficiaryId}/documents`](/api-reference/fx-payment/beneficiaries/submit-beneficiary-document). | Uploaded filename | Outcome | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `reject.pdf`, `custody-reject.png`, … | The custody-attestation check fails and the instruction is rejected; [`BENEFICIARY_INSTRUCTION_REJECTED`](/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-rejected) fires after the review settles, with the reason on `instruction.currentState.reason.message`. | Put `reject` in the file's name, not its extension, and keep the letters together; a space or symbol between them (`rej ect`) breaks the match. Any other filename follows the normal screening path. ## Deposits In sandbox, the deposit amount decides its QR code's outcome: | Deposit amount (`sourceAmount`) | Outcome | | ------------------------------- | ------------------------- | | `8.00` | The QR code is never paid | | `9.00` | The QR code expires | | Any other amount | The QR code is paid | ## Crypto test tokens The sandbox uses a fixed set of test tokens for crypto deposits, swaps, and withdrawals. These are mock ERC-20 contracts that Trace Finance controls. They mirror the shape of the real assets but hold no monetary value, so you can move them freely during homologation. Everything in the sandbox runs on the **Ethereum testnet (Sepolia)**, never on mainnet. The API always reports the network as `ETHEREUM`, the same value it uses in production, but in the sandbox that value resolves to Sepolia. The contract addresses below are Sepolia deployments and carry no value. Never treat them as the real `USDT`, `USDC`, or `BRL` tokens. Only the **`ETHEREUM`** network is available in the sandbox today. Other networks are not yet supported for sandbox testing. | Name | `symbol` | `network` | `contractAddress` | `decimals` | | ----------------- | -------- | ---------------------------- | -------------------------------------------- | ---------- | | Trace Finance BRL | `BRLT` | `ETHEREUM` (Sepolia testnet) | `0x2588a80f342ea4f24c91c0148a3d6e832ce9dca9` | 6 | | Trace Mock USDT | `USDT` | `ETHEREUM` (Sepolia testnet) | `0x164afc6456b21b4b1bd505a69be35cc2d9692fd8` | 6 | | Trace Mock USDC | `USDC` | `ETHEREUM` (Sepolia testnet) | `0xca732283fd24c588d9084b1ae357b540e9e7b2f3` | 6 | ## Related * [Environments](/guides/environments) — sandbox and production base URLs * [Crypto assets and networks](/guides/crypto-assets) — production stablecoins, networks, and contract addresses * [Beneficiaries](/guides/compliance/beneficiaries) — what gets screened and what stays on your side * [Register a beneficiary](/journeys/register-beneficiary) — step-by-step procedure for submitting beneficiaries and payment instructions * [Deposit](/journeys/deposit) — end-to-end deposit with a PIX dynamic QR code * [Test webhooks in sandbox](/webhooks/test-in-sandbox) — develop and verify webhook handlers # Home Source: https://docs.tracefinance.com/index Explore guides, examples, and API references to build with Trace Finance. ## Get started Set up your sandbox and make your first API call in minutes. Learn how to authenticate requests with bearer tokens. Understand sandbox and production environments. ## Build with Trace Finance Create accounts that hold fiat and crypto for your account owners. Accept deposits via PIX, wire transfer, and crypto networks. Process withdrawals to bank accounts and crypto wallets. Exchange between BRL, USDT, and USDC. # Deposit Source: https://docs.tracefinance.com/journeys/deposit Step-by-step guide to crediting an account with a PIX dynamic QR code. ## Overview Deposits credit an account when funds arrive. The customer references a quote that locks the rate (or a 1:1 spot for same-asset) and picks a funding rail; the response returns the concrete funding instruction — a PIX dynamic QR code — the payer uses to send money in. The deposit transitions to `COMPLETED` once the inbound payment is reconciled. ## Prerequisites * An [active account](/journeys/open-account) configured for the asset to be credited. * Valid [authentication credentials](/guides/authentication). ## Steps Quotes lock the FX rate (or a 1:1 spot for same-asset) for a short window and are bound to one account. Use `sourceAsset` for the asset paid in and `targetAsset` for the asset credited to the account. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/quotes \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "accountId": "", "sourceAsset": "BRL", "targetAsset": "BRL", "sourceAmount": "500.00" }' ``` The response returns the quote `id`, the locked `effectiveRate`, and `expiresAt`. The quote can be consumed by exactly one operation before it expires. Reference the account, the quote, and the funding rail the payer will use to send the money in. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/operations/deposits \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "accountId": "", "quoteId": "", "fundingInstruction": { "rail": "PIX_DYNAMIC_QR_CODE" } }' ``` Returns `201` with the operation in `REQUESTED` status. The response carries `intent.fundingInstruction` with the concrete details for the rail — for `PIX_DYNAMIC_QR_CODE` that's the `qrCode` payload (EMV-encoded BR Code) and its `expiresAt`. The funding instruction is generated asynchronously, so `intent.fundingInstruction` may be `null` on the initial response and populated shortly after. Present the `qrCode` from `intent.fundingInstruction` to the payer to pay via PIX before its `expiresAt`. The amount must match the quote's `sourceAmount`. Subscribe to `OPERATION_COMPLETED` to receive the deposit confirmation once the inbound payment is reconciled, or `OPERATION_FAILED` if reconciliation fails. Both deliver the same payload as `OPERATION_REQUESTED`, with `currentState.status` set to the new status (and `currentState.reason` populated on failure). The intermediate `PROCESSING` status is not published as a webhook; poll `GET /v1/operations/{operationId}` if you need to surface it in your UI. ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/operations/ \ --header 'Authorization: Bearer ' ``` ## What happens next * [Execute a swap](/journeys/swap) — convert the deposited asset within the account. * [Make a withdrawal](/journeys/withdrawal) — send funds out. # Open an account Source: https://docs.tracefinance.com/journeys/open-account Choose between a named account held in the owner's name and a Trace Finance-issued virtual account, then follow the matching journey. ## Overview Every account in Trace Finance is multi-currency: crypto support is always included, and you declare which fiat assets (like BRL) to enable when you create the account. For each fiat asset you also choose an account type, and that choice determines who holds the account and which compliance documents apply. There are two fiat account types: * **Named accounts** are held in the account owner's name. Use `NAMED` for residents and `NAMED_NON_RESIDENT` for non-residents. * **Virtual accounts** are issued by Trace Finance. Use `VIRTUAL`. The onboarding mechanics are the same for both. You create the account, upload documents, register beneficial owners for company accounts, submit for review, and retrieve funding instructions once each asset activates. What changes is the account type you request and the set of compliance documents you provide. ## Prerequisites * Valid [authentication credentials](/guides/authentication). * Account owner details: legal name (or first and last name for an individual), identification document (a tax ID such as CNPJ or CPF), industry, and address. ## Steps Pick the account type that matches how the funds will be held, then follow that journey end to end. | Attribute | Named account | Virtual account | | ----------------- | ----------------------------------------------------------- | --------------------------------------------------------------- | | Who holds it | The account owner (`NAMED`, `NAMED_NON_RESIDENT`) | Trace Finance (`VIRTUAL`) | | Typical use | Funds held in the owner's own name | Own treasury, intercompany flows, proprietary commerce | | Compliance | Baseline plus segment-specific documents | Baseline only, unless the owner is a regulated payment entity | | Full requirements | [BRL named accounts](/guides/compliance/brl-named-accounts) | [BRL virtual accounts](/guides/compliance/brl-virtual-accounts) | An account held in the owner's name, with baseline plus segment-specific compliance. A Trace Finance-issued account for your own treasury and intercompany flows. ## What happens next * [Make a deposit](/journeys/deposit) — fund the account with BRL or crypto. * [Execute a swap](/journeys/swap) — convert between the account's assets. # Open a named account Source: https://docs.tracefinance.com/journeys/open-named-account Step-by-step guide to opening a BRL account held in the account owner's name. ## Overview A named account is a fiat account held in the account owner's name. Use `NAMED` for Brazilian residents and `NAMED_NON_RESIDENT` for non-residents. Every account is multi-currency, so crypto support is always included alongside the fiat assets you enable. Named accounts follow Trace Finance's compliance baseline and add segment-specific information and documents on top, depending on the owner's profile (payment facilitators, non-residents, startups, crypto businesses, funds, and exchange operators). The exact set for your account is always returned in `requirements.currentlyDue` when you create it. See [BRL named accounts](/guides/compliance/brl-named-accounts) for the full checklist. ## Prerequisites * Valid [authentication credentials](/guides/authentication). * Account owner details: legal name (or first and last name for an individual), identification document (a tax ID such as CNPJ or CPF), industry, expected monthly volume, and address. * The document checklist for the owner's segment. See [BRL named accounts](/guides/compliance/brl-named-accounts). The documents that apply to your account appear in `requirements.currentlyDue` after creation. ## Steps Submit a request with the fiat assets to enable and the account owner's details. Set each fiat asset's `accountType` to `NAMED`, or to `NAMED_NON_RESIDENT` for a non-resident owner. Crypto support is always included, so you don't declare it or set its `accountType`. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/accounts \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "assets": [ { "code": "BRL", "accountType": "NAMED" } ], "owner": { "type": "COMPANY", "legalName": "Acme Ltda", "identificationDocument": { "value": "11222333000181", "type": "CNPJ" }, "industry": "SOFTWARE_DEVELOPMENT", "expectedMonthlyVolume": "FROM_0_TO_50000", "incorporateDate": "2018-05-12", "isStartup": false, "address": { "addressLine1": "Rua das Flores, 100", "addressLine2": "Suite 456", "city": "São Paulo", "state": "SP", "country": "BR", "postalCode": "01234-567" } } }' ``` The response returns the account in `ACTION_REQUIRED` status, with the document types required for onboarding listed under `requirements.currentlyDue`. For an individual owner, set `owner.type` to `INDIVIDUAL` and provide `firstName` and `lastName` instead of `legalName`. To fix a rejected field after creation, use [`PATCH /v1/accounts/{accountId}`](/api-reference/fx-account/accounts/update-account) to replace the owner profile before submitting for review. Named accounts often require additional segment-specific documents, for example for payment facilitators, non-residents, startups, crypto businesses, funds, or exchange operators. Whatever applies to your account is listed in `requirements.currentlyDue`. See [BRL named accounts](/guides/compliance/brl-named-accounts) for the full breakdown by segment. The creation response includes a `requirements.currentlyDue` array listing every document you need to provide. Each item carries a `type` discriminator. `ACCOUNT_DOCUMENT` and `UBO_DOCUMENT` name a specific `documentType`, while `IDENTITY_VERIFICATION` and `INCORPORATION_DOCUMENT` are grouped requirements whose `options` array lists the acceptable document types together with the `subTypes` and `requiredMetadata` each option needs. Upload documents against the `/v1/accounts/{accountId}/documents` endpoint, setting `holder.type` to `ACCOUNT` and `holder.referenceId` to the account ID: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/accounts//documents \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --form 'body={"documentType":"ARTICLES_OF_ASSOCIATION","holder":{"type":"ACCOUNT","referenceId":""}};type=application/json' \ --form 'file=@/path/to/articles-of-association.pdf' ``` Returns `204 No Content`. Upload one document per request, repeating for every requirement listed in `currentlyDue`. Company-owned accounts require at least one beneficial owner (UBO). Register each UBO with their identity, contact, and address details. `phone` and `email` are required so we can reach the UBO for follow-up during compliance review. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/accounts//ubos \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "João Silva", "identificationDocument": { "value": "12345678909", "type": "CPF" }, "address": { "addressLine1": "Rua das Flores, 100", "city": "São Paulo", "state": "SP", "country": "BR", "postalCode": "01234-567" }, "phone": "5511999999999", "email": "joao.silva@example.com", "ownershipPercentage": 50.0, "isLegalRepresentative": false }' ``` The response includes the UBO's `id`, which you'll need for uploading their documents. Individual accounts have no UBOs, so skip this step and the next one. Each UBO needs identity documents. Use the same `/v1/accounts/{accountId}/documents` endpoint, this time setting `holder.type` to `UBO` and `holder.referenceId` to the UBO's `id`. Identity documents typically require a `documentSubType` (for example `FRONT_SIDE` or `BACK_SIDE`) and a `metadata.country`: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/accounts//documents \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --form 'body={"documentType":"ID_CARD","documentSubType":"FRONT_SIDE","holder":{"type":"UBO","referenceId":""},"metadata":{"country":"BR"}};type=application/json' \ --form 'file=@/path/to/ubo-id-front.pdf' ``` Returns `204 No Content`. Repeat for every side and every UBO registered on the account. Once all required documents are uploaded, submit the account for compliance review. This endpoint has no request body: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/accounts//review \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' ``` Returns `202 Accepted`. The account transitions from `ACTION_REQUIRED` to `REVIEWING`. If any documents are still missing or rejected, the API returns `422` with a `MISSING_DOCUMENTS_FOR_REVIEW` error listing the outstanding `documentType` names. After review is approved, each asset activates on its own pipeline. The crypto wallet always activates first, since it has no manual steps, but the crypto wallet activating does not move the account to `ACTIVE`. The account moves through `OPENING` and becomes `ACTIVE` only once a fiat asset finishes onboarding. Subscribe to the [`ACCOUNT_ASSET_ACTIVATED`](/api-reference/fx-webhook/events/account/account-asset-activated) and [`ACCOUNT_ASSET_FAILED`](/api-reference/fx-webhook/events/account/account-asset-failed) webhooks to react as each asset finishes, or poll [`GET /v1/accounts/{accountId}`](/api-reference/fx-account/accounts/get-account). Each entry in the response's `assets` array carries its own provider-account `status` (`ONBOARDING`, `ACTIVE`, `FAILED`, `FROZEN`, `DEACTIVATED`) so you can see which assets are ready. Once the account is active, retrieve the incoming transfer details for each active asset: ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/accounts//fundingInstructions \ --header 'Authorization: Bearer ' ``` ```json Response theme={"theme":"tokyo-night"} [ { "asset": "BRL", "type": "PIX_KEY", "rail": "PIX_KEY", "keyType": "CNPJ", "key": "12345678000101", "accountIdentifier": null } ] ``` For a named account, the BRL Pix key is registered in the account owner's name, so `keyType` reflects the owner's tax ID (`CNPJ` for a company, `CPF` for an individual). Filter the catalog with the optional `asset` and `rail` query parameters when you only need one rail for an asset. ## What happens next * [Make a deposit](/journeys/deposit) — fund the account with BRL or crypto. * [Execute a swap](/journeys/swap) — convert between the account's assets. # Open a virtual account Source: https://docs.tracefinance.com/journeys/open-virtual-account Step-by-step guide to opening a Trace Finance-issued BRL virtual account. ## Overview A virtual account is a fiat account issued directly by Trace Finance. It suits customers who run their own treasury, intercompany flows, or proprietary commerce on Trace Finance. Use `VIRTUAL` as the account type. Every account is multi-currency, so crypto support is always included alongside the fiat assets you enable. Verification for a virtual account follows Trace Finance's compliance baseline only, which is a lighter document set than a named account. Additional documents apply only when the owner is itself a regulated payment entity, such as a payment service provider, a banking-as-a-service provider, or a virtual-asset service provider. The exact set for your account is always returned in `requirements.currentlyDue` when you create it. See [BRL virtual accounts](/guides/compliance/brl-virtual-accounts) for the full checklist. ## Prerequisites * Valid [authentication credentials](/guides/authentication). * Account owner details: legal name (or first and last name for an individual), identification document (a tax ID such as CNPJ or CPF, or a passport), industry, expected monthly volume, and address. * The baseline document checklist. See [BRL virtual accounts](/guides/compliance/brl-virtual-accounts). The documents that apply to your account appear in `requirements.currentlyDue` after creation. ## Steps Submit a request with the fiat assets to enable and the account owner's details. Set each fiat asset's `accountType` to `VIRTUAL`. Crypto support is always included, so you don't declare it or set its `accountType`. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/accounts \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "assets": [ { "code": "BRL", "accountType": "VIRTUAL" } ], "owner": { "type": "COMPANY", "legalName": "Acme Ltda", "identificationDocument": { "value": "11222333000181", "type": "CNPJ" }, "industry": "SOFTWARE_DEVELOPMENT", "expectedMonthlyVolume": "FROM_0_TO_50000", "incorporateDate": "2018-05-12", "isStartup": false, "address": { "addressLine1": "Rua das Flores, 100", "addressLine2": "Suite 456", "city": "São Paulo", "state": "SP", "country": "BR", "postalCode": "01234-567" } } }' ``` The response returns the account in `ACTION_REQUIRED` status, with the document types required for onboarding listed under `requirements.currentlyDue`. For an individual owner, set `owner.type` to `INDIVIDUAL` and provide `firstName` and `lastName` instead of `legalName`. To fix a rejected field after creation, use [`PATCH /v1/accounts/{accountId}`](/api-reference/fx-account/accounts/update-account) to replace the owner profile before submitting for review. The owner's `identificationDocument` accepts any supported document type. A tax-id type (such as `CNPJ` or `CPF`) implies its country, so no `country` is needed. Virtual accounts also accept a non-fiscal `PASSPORT` document, in which case you must supply a `country`: ```json theme={"theme":"tokyo-night"} "identificationDocument": { "value": "A1234567", "type": "PASSPORT", "country": "US" } ``` A virtual account's document set is the compliance baseline. You only provide additional documents when the owner handles third-party funds as a regulated payment entity. Whatever applies is listed in `requirements.currentlyDue`. See [BRL virtual accounts](/guides/compliance/brl-virtual-accounts) for details. The creation response includes a `requirements.currentlyDue` array listing every document you need to provide. Each item carries a `type` discriminator. `ACCOUNT_DOCUMENT` and `UBO_DOCUMENT` name a specific `documentType`, while `IDENTITY_VERIFICATION` and `INCORPORATION_DOCUMENT` are grouped requirements whose `options` array lists the acceptable document types together with the `subTypes` and `requiredMetadata` each option needs. Upload documents against the `/v1/accounts/{accountId}/documents` endpoint, setting `holder.type` to `ACCOUNT` and `holder.referenceId` to the account ID: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/accounts//documents \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --form 'body={"documentType":"ARTICLES_OF_ASSOCIATION","holder":{"type":"ACCOUNT","referenceId":""}};type=application/json' \ --form 'file=@/path/to/articles-of-association.pdf' ``` Returns `204 No Content`. Upload one document per request, repeating for every requirement listed in `currentlyDue`. Company-owned accounts require at least one beneficial owner (UBO). Register each UBO with their identity, contact, and address details. `phone` and `email` are required so we can reach the UBO for follow-up during compliance review. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/accounts//ubos \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "name": "João Silva", "identificationDocument": { "value": "12345678909", "type": "CPF" }, "address": { "addressLine1": "Rua das Flores, 100", "city": "São Paulo", "state": "SP", "country": "BR", "postalCode": "01234-567" }, "phone": "5511999999999", "email": "joao.silva@example.com", "ownershipPercentage": 50.0, "isLegalRepresentative": false }' ``` The response includes the UBO's `id`, which you'll need for uploading their documents. Individual accounts have no UBOs, so skip this step and the next one. Each UBO needs identity documents. Use the same `/v1/accounts/{accountId}/documents` endpoint, this time setting `holder.type` to `UBO` and `holder.referenceId` to the UBO's `id`. Identity documents typically require a `documentSubType` (for example `FRONT_SIDE` or `BACK_SIDE`) and a `metadata.country`: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/accounts//documents \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --form 'body={"documentType":"ID_CARD","documentSubType":"FRONT_SIDE","holder":{"type":"UBO","referenceId":""},"metadata":{"country":"BR"}};type=application/json' \ --form 'file=@/path/to/ubo-id-front.pdf' ``` Returns `204 No Content`. Repeat for every side and every UBO registered on the account. Once all required documents are uploaded, submit the account for compliance review. This endpoint has no request body: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/accounts//review \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' ``` Returns `202 Accepted`. The account transitions from `ACTION_REQUIRED` to `REVIEWING`. If any documents are still missing or rejected, the API returns `422` with a `MISSING_DOCUMENTS_FOR_REVIEW` error listing the outstanding `documentType` names. After review is approved, each asset activates on its own pipeline. The crypto wallet always activates first, since it has no manual steps, but the crypto wallet activating does not move the account to `ACTIVE`. The account moves through `OPENING` and becomes `ACTIVE` only once a fiat asset finishes onboarding. Subscribe to the [`ACCOUNT_ASSET_ACTIVATED`](/api-reference/fx-webhook/events/account/account-asset-activated) and [`ACCOUNT_ASSET_FAILED`](/api-reference/fx-webhook/events/account/account-asset-failed) webhooks to react as each asset finishes, or poll [`GET /v1/accounts/{accountId}`](/api-reference/fx-account/accounts/get-account). Each entry in the response's `assets` array carries its own provider-account `status` (`ONBOARDING`, `ACTIVE`, `FAILED`, `FROZEN`, `DEACTIVATED`) so you can see which assets are ready. Once the account is active, retrieve the incoming transfer details for each active asset: ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/accounts//fundingInstructions \ --header 'Authorization: Bearer ' ``` ```json Response theme={"theme":"tokyo-night"} [ { "asset": "BRL", "type": "PIX_KEY", "rail": "PIX_KEY", "keyType": "RANDOM", "key": "b3f1c2a4-9d8e-4f2a-8c1b-2e5f7a9c0d13", "accountIdentifier": "VA-000123" } ] ``` For a virtual account, the Pix key is issued by Trace Finance rather than the account owner. Trace may share one key across virtual accounts, so route inbound funds using the `accountIdentifier` returned alongside the key. Filter the catalog with the optional `asset` and `rail` query parameters when you only need one rail for an asset. ## What happens next * [Make a deposit](/journeys/deposit) — fund the account with BRL or crypto. * [Execute a swap](/journeys/swap) — convert between the account's assets. # Register a beneficiary Source: https://docs.tracefinance.com/journeys/register-beneficiary Submit a beneficiary and a payment instruction, then track the compliance review to APPROVED. ## Overview Beneficiaries are the end-users your platform pays out to. To withdraw funds to one, you first register the beneficiary together with at least one payment instruction (PIX dict key, bank account, or crypto wallet). The payment instruction is reviewed asynchronously by Trace Finance — only `APPROVED` instructions can be referenced by a withdrawal. The beneficiary record itself has no status: it is created once and reused. Each payment instruction is reviewed individually. ## Prerequisites * An [active account](/journeys/open-account) that will fund the eventual withdrawals. * Valid [authentication credentials](/guides/authentication). * A [webhook subscription](/webhooks/subscribe) for `BENEFICIARY_INSTRUCTION_APPROVED` and `BENEFICIARY_INSTRUCTION_REJECTED` (recommended — the review is asynchronous). * The entity data you collected during your own end-user KYC. See [Beneficiary compliance](/guides/compliance/beneficiaries) for what Trace Finance reviews and what stays on your side. ## Steps The request carries both the entity identity and the first payment instruction in one call. The entity fields are identity-equivalent to a KYC submission; the payment instruction is rail-specific. | Field | Format | Notes | | --------------------------------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------ | | `entity.type` | enum | `INDIVIDUAL` or `COMPANY`. | | `entity.firstName` / `lastName` | string | Required when `type = INDIVIDUAL`. | | `entity.legalName` | string | Required when `type = COMPANY`. | | `entity.tradeName` | string | Optional. | | `entity.identificationDocument.value` / `identificationDocument.type` | string / enum | CPF/CNPJ for Brazilian beneficiaries; a foreign tax id or non-fiscal `PASSPORT` otherwise. | | `entity.identificationDocument.country` | ISO 3166-1 alpha-2 | Required only for `PASSPORT`; implied by the type for tax ids. | | `entity.birthDate` | `yyyy-MM-dd` | Required when `type = INDIVIDUAL`. | | `entity.address` | object | Residential or registered address. | | `relationshipType` | enum | `SELF_OWNED` or `THIRD_PARTY`. | | `paymentInstruction` | object | Rail-specific (PIX dict key, crypto wallet, etc.). | The entity's `identificationDocument` accepts any supported document type. A tax-id type (such as `CPF` or `CNPJ`) implies its country, so no `country` is needed. A foreign natural person without a tax id can use a non-fiscal `PASSPORT`, in which case you must supply a `country`: ```json theme={"theme":"tokyo-night"} "identificationDocument": { "value": "A1234567", "type": "PASSPORT", "country": "US" } ``` ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/beneficiaries \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "entity": { "type": "INDIVIDUAL", "firstName": "John", "lastName": "Doe", "identificationDocument": { "value": "12345678901", "type": "CPF" }, "birthDate": "1990-01-15" }, "relationshipType": "THIRD_PARTY", "paymentInstruction": { "rail": "PIX_KEY", "asset": "BRL", "dictKeyType": "CPF", "dictKey": "12345678901" } }' ``` The response returns the beneficiary with its `id` and the created `paymentInstruction.id` in `PENDING_REVIEW` status. A `BENEFICIARY_INSTRUCTION_CREATED` webhook fires immediately. The first payment instruction on a new beneficiary triggers the full compliance review described in [Beneficiary compliance](/guides/compliance/beneficiaries). The review resolves asynchronously — typically within seconds — to `APPROVED` or `REJECTED`, delivered as a `BENEFICIARY_INSTRUCTION_APPROVED` or `BENEFICIARY_INSTRUCTION_REJECTED` webhook. If you cannot subscribe to webhooks, poll the beneficiary endpoint until the instruction settles: ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/beneficiaries/ \ --header 'Authorization: Bearer ' ``` On `REJECTED`, the webhook payload includes a `currentState.reason` describing why. The instruction cannot be reused — fix the data and submit a new instruction (see next step), or register a new beneficiary if the entity data was wrong. To force a rejection in sandbox while you build your handler, see [Testing in sandbox → Force a payment-instruction rejection](/guides/testing-in-sandbox#force-a-payment-instruction-rejection) — it shows the full request you send and the `BENEFICIARY_INSTRUCTION_REJECTED` payload your handler will receive. A crypto payment instruction requires a custody attestation document to complete its compliance review. Attach it inline when creating the beneficiary, or upload it afterwards while the instruction is still under review. The endpoint is scoped to the beneficiary; set the payment instruction as the document `holder` in the `body`. The document is uploaded as multipart: a JSON `body` part carrying the `documentType` and `holder`, plus the binary `file`: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/beneficiaries//documents \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --form 'body={"documentType":"CUSTODY_ATTESTATION","holder":{"type":"PAYMENT_INSTRUCTION","referenceId":""}};type=application/json' \ --form 'file=@custody-attestation.pdf' ``` Trace Finance stores the file and forwards it to the compliance review. Once accepted, the crypto instruction can settle to `APPROVED`. If compliance asks for a clearer file, upload the same `documentType` again while the instruction is still under review. That replaces the file instead of adding a second document: the document keeps its `referenceId`, its `version` increases, and the review restarts on the new file. Previous versions are retained. A reviewer can send a document back instead of deciding. The instruction moves to `ACTION_REQUIRED` and you get a `BENEFICIARY_INSTRUCTION_ACTION_REQUIRED` webhook. This is a request, not a verdict — the same instruction can still be approved. Which file to replace is in `instruction.documents[]`: the one whose `currentState.status` is `ACTION_REQUIRED`, with its own `currentState.reason` (for example, "Send a colour scan — this one is illegible"). Upload the replacement exactly as in the previous step, with the same instruction as `holder`. Compliance can ask about more than one document. The instruction stays `ACTION_REQUIRED` until **none** of its documents is, so replacing one file may not end the wait — always read the per-document state rather than assuming one upload settles the review. The instruction returns to `PENDING_REVIEW` on its own once nothing is outstanding. A beneficiary can hold multiple payment instructions across rails. Add another one without re-submitting the entity data: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/beneficiaries//payment-instructions \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "rail": "PIX_KEY", "asset": "BRL", "dictKeyType": "EMAIL", "dictKey": "john.doe@example.com" }' ``` Subsequent instructions on the same beneficiary go through a reduced compliance check — the entity has already been screened, so only the new payment-instrument is verified. ## What happens next * [Withdraw](/journeys/withdrawal) — use the `APPROVED` beneficiary and payment instruction to send funds. * [Beneficiary compliance](/guides/compliance/beneficiaries) — what Trace Finance reviews on each instruction and what remains your responsibility. * [Verify webhook signatures](/webhooks/verify-signatures) — confirm review-outcome webhooks came from Trace Finance. # Swap Source: https://docs.tracefinance.com/journeys/swap Step-by-step guide to converting funds between assets within an account. ## Overview Swaps convert funds between assets within the same account — for example, BRL to USDT or USDC to BRL. Every swap references a quote that locks the FX rate. Once executed, a swap cannot be reversed. ## Prerequisites * An [active account](/journeys/open-account) with sufficient balance in the quote's source asset. * Valid [authentication credentials](/guides/authentication). ## Steps Specify the source asset, target asset, and either the amount you want to spend (`sourceAmount`) or the amount you want to receive (`targetAmount`). ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/quotes \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "accountId": "", "sourceAsset": "BRL", "targetAsset": "USDT", "sourceAmount": "500.00" }' ``` The response returns the quote `id`, the locked `effectiveRate`, the computed `targetAmount`, and `expiresAt`. The quote is bound to the account and can be consumed by exactly one operation before it expires. Reference the account and the quote. No other inputs are needed — source and target assets and amounts are derived from the quote. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/operations/swaps \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "accountId": "", "quoteId": "" }' ``` Returns `201` immediately with the operation in `REQUESTED` status. Settlement happens asynchronously. Subscribe to `OPERATION_COMPLETED` and `OPERATION_FAILED` to receive the terminal outcome — both deliver the same payload as `OPERATION_REQUESTED`, with `currentState.status` set to the new status (and `currentState.reason` populated on failure). The intermediate `PROCESSING` status is not published as a webhook; poll `GET /v1/operations/{operationId}` if you need to surface it in your UI. ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/operations/ \ --header 'Authorization: Bearer ' ``` Swaps are forward-only. Once executed at the quoted rate, they cannot be reversed. Verify the `effectiveRate` on the quote before creating the swap. ## What happens next * [Make a withdrawal](/journeys/withdrawal) — send the converted funds out of the account. # Transfer Source: https://docs.tracefinance.com/journeys/transfer Step-by-step guide to moving funds between two accounts of the same customer. ## Overview Transfers move funds between two accounts owned by the same customer. They are same-asset — the same value is debited from the source account and credited to the target account, with no FX conversion. The system generates a 1:1 quote internally and returns it on the response; you do not need to create a quote first. To move between assets, [execute a swap](/journeys/swap) and then transfer. ## Prerequisites * Two [active accounts](/journeys/open-account) owned by the same customer, both with the requested asset enabled. The source must hold sufficient balance. * Valid [authentication credentials](/guides/authentication). ## Steps Reference the source account, the target account, and the amount to move. Source and target must differ; the target account must be in `ACTIVE` status with the requested asset enabled. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/operations/transfers \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "sourceAccountId": "", "targetAccountId": "", "amount": { "value": "500.00", "asset": "BRL" } }' ``` Returns `201` immediately with the operation in `REQUESTED` status. Settlement happens asynchronously. Subscribe to `OPERATION_COMPLETED` and `OPERATION_FAILED` to receive the terminal outcome — both deliver the same payload as `OPERATION_REQUESTED`, with `currentState.status` set to the new status (and `currentState.reason` populated on failure). The intermediate `PROCESSING` status is not published as a webhook; poll `GET /v1/operations/{operationId}` if you need to surface it in your UI. ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/operations/ \ --header 'Authorization: Bearer ' ``` Cross-customer transfers are not supported — both accounts must belong to the same customer. If the target account belongs to another customer, the API returns `404 RESOURCE_NOT_FOUND`. ## What happens next * [Execute a swap](/journeys/swap) — convert between assets within a single account. * [Make a withdrawal](/journeys/withdrawal) — send funds out to an external beneficiary. # Withdraw Source: https://docs.tracefinance.com/journeys/withdrawal Step-by-step guide to sending funds from an account to a registered beneficiary. ## Overview Withdrawals move funds out of an account to an external destination — a bank account (PIX, TED) or a crypto wallet. Every withdrawal references a quote that locks the FX rate (or a 1:1 spot for same-asset) and a previously approved beneficiary with at least one payment instruction. ## Prerequisites * An [active account](/journeys/open-account) with sufficient balance in the quote's source asset. * Valid [authentication credentials](/guides/authentication). ## Steps Submit the entity details and the payment instruction (PIX, bank account, or crypto wallet). The beneficiary record itself has no status — it is created once and reused across destinations. Each payment instruction is reviewed individually: it starts in `PENDING_REVIEW` on creation and transitions asynchronously to `APPROVED` or `REJECTED`. The first instruction on a new beneficiary triggers the full compliance review; instructions added later to the same beneficiary go through a reduced check. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/beneficiaries \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "entity": { "type": "INDIVIDUAL", "firstName": "John", "lastName": "Doe", "identificationDocument": { "value": "12345678901", "type": "CPF" }, "birthDate": "1990-01-15" }, "relationshipType": "THIRD_PARTY", "paymentInstruction": { "rail": "PIX_KEY", "asset": "BRL", "dictKeyType": "CPF", "dictKey": "12345678901" } }' ``` The response returns the beneficiary with its `id` and the created `paymentInstruction.id`. A `BENEFICIARY_INSTRUCTION_CREATED` webhook fires immediately, then either `BENEFICIARY_INSTRUCTION_APPROVED` or `BENEFICIARY_INSTRUCTION_REJECTED` once the review completes — subscribe to track the outcome. Only `APPROVED` instructions can be used in a withdrawal. Quotes lock the FX rate (or a 1:1 spot for same-asset) for a short window and are bound to one account. Provide either `sourceAmount` or `targetAmount`, never both. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/quotes \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "accountId": "", "sourceAsset": "BRL", "targetAsset": "BRL", "sourceAmount": "500.00" }' ``` The response returns the quote `id`, the locked `effectiveRate`, and `expiresAt`. The quote can be consumed by exactly one operation before it expires. Reference the approved beneficiary, the chosen payment instruction on that beneficiary, and the quote. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/operations/withdrawals \ --header 'Authorization: Bearer ' \ --header 'X-Idempotency-Key: ' \ --header 'Content-Type: application/json' \ --data '{ "accountId": "", "quoteId": "", "beneficiary": { "mode": "REFERENCE", "id": "", "paymentInstruction": { "mode": "REFERENCE", "id": "" } } }' ``` Returns `201` immediately with the operation in `REQUESTED` status. Settlement and rail dispatch happen asynchronously. Subscribe to `OPERATION_COMPLETED` and `OPERATION_FAILED` to receive the terminal outcome — both deliver the same payload as `OPERATION_REQUESTED`, with `currentState.status` set to the new status (and `currentState.reason` populated on failure). If the operation pauses waiting on an action from you, `OPERATION_ACTION_REQUIRED` fires and processing resumes once the pending action is resolved. The intermediate `PROCESSING` status is not published as a webhook; poll `GET /v1/operations/{operationId}` if you need to surface it in your UI. ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/operations/ \ --header 'Authorization: Bearer ' ``` On `FAILED`, the operation's `currentState.reason` carries a code and message describing the cause. To exercise this branch in sandbox before you ship, see [Testing in sandbox → Force a withdrawal failure](/guides/testing-in-sandbox#force-a-withdrawal-failure) — it shows the withdrawal request you submit and the `OPERATION_FAILED` payload your handler will receive for each of the simulated rail errors. ## What happens next * [Make a deposit](/journeys/deposit) — fund an account before withdrawing. * [Execute a swap](/journeys/swap) — convert between assets before sending out. # Quickstart Source: https://docs.tracefinance.com/quickstart Make your first API request to Trace Finance in under five minutes. ## Prerequisites * A **client ID** and **client secret** provided during onboarding. * [curl](https://curl.se/) or any HTTP client. ## Steps Follow the [Authentication](/guides/authentication) guide to exchange your client credentials for a JWT. Copy the `access_token` from the response. List accounts in the sandbox: ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/accounts \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' ``` You should see a paginated response: ```json theme={"theme":"tokyo-night"} { "data": [], "meta": { "previousCursor": null, "nextCursor": null, "total": 0 } } ``` An empty `data` array is expected — you haven't created any accounts yet. You're connected and authenticated. ## What's next * [Open an account](/journeys/open-account) — create your first account * [Authentication](/guides/authentication) — learn about token management * [Environments](/guides/environments) — understand sandbox vs production # Webhooks Source: https://docs.tracefinance.com/webhooks/overview Receive real-time event notifications from the Trace Finance platform. ## Overview Webhooks let Trace Finance push events to your backend as they happen — account onboarding completions, beneficiary review outcomes, payment operation updates, and more. Instead of polling for state changes, you register an HTTPS endpoint and Trace Finance `POST`s a JSON payload whenever a relevant event fires. A typical integration looks like: 1. Stand up an HTTPS endpoint on your side that accepts `POST` requests. 2. Register the URL with the [Subscriptions API](/api-reference/fx-webhook/subscriptions/create-subscription), choosing the resource and event types you want. 3. Verify each request's signature, process the payload, and return a `2xx` response. 4. Trace Finance retries failed deliveries and exposes [execution logs](/api-reference/fx-webhook/subscriptions/list-execution-logs) for inspection and manual replay. ## Setup Webhook subscriptions are managed through the [Subscriptions API](/api-reference/fx-webhook/subscriptions/create-subscription). Each subscription binds one URL to one or more resources (`ACCOUNT`, `OPERATION`, `BENEFICIARY`) and optionally narrows delivery to specific event types per resource. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/subscriptions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "url": "https://api.example.com/trace-webhooks", "resources": [ { "name": "OPERATION", "includeAll": true } ], "allowRetry": true }' ``` See [Subscribe to events](/webhooks/subscribe) for the full setup walkthrough — including how to manage multiple subscriptions, scope event types, and pause delivery. ## Signatures Every outbound request carries a `X-Message-Signature` header containing an HMAC-SHA256 signature you use to verify the message came from Trace Finance. The signature is computed over `${messageId}+${clientId}` with your client secret as the key: ```text theme={"theme":"tokyo-night"} X-Message-Signature: ``` Other headers you can rely on: | Header | Purpose | | ----------------- | --------------------------------------------------------------------- | | `X-Message-Id` | Unique delivery identifier — also a UUID for idempotency on your side | | `X-Company-Id` | Your Trace Finance company identifier | | `X-Event-Type` | Event type (e.g., `OPERATION_REQUESTED`) | | `X-Resource-Name` | Resource group (e.g., `OPERATION`) | See [Verify signatures](/webhooks/verify-signatures) for verification code in Python, JavaScript, and Go. ## Retry policy If your endpoint returns a non-`2xx` status or fails to respond, Trace Finance queues the delivery for retry with a delay between attempts. Each delivery attempt is recorded in an [execution log](/api-reference/fx-webhook/subscriptions/list-execution-logs) you can inspect or [resend manually](/api-reference/fx-webhook/subscriptions/resend-delivery). See [Retry policy](/webhooks/retry-policy) for the exact retry budget, log lifetime, and replay guidance. ## Available events Browse the full event catalog by resource: | Resource | Events | Description | | --------------------------------------------------------------------------------------------------- | ------ | ---------------------------------------------------------------------------- | | [Account](/api-reference/fx-webhook/events/account/account-created) | 4 | Account creation, action-required transitions, and asset onboarding outcomes | | [Operation](/api-reference/fx-webhook/events/operation/operation-requested) | 4 | Operation creation, customer action required, completion, and failure | | [Beneficiary](/api-reference/fx-webhook/events/beneficiary/beneficiary-payment-instruction-created) | 3 | Payment instruction creation, approval, and rejection | Operations publish webhooks on creation (`OPERATION_REQUESTED`), when paused waiting on your action (`OPERATION_ACTION_REQUIRED`), and on terminal outcomes (`OPERATION_COMPLETED`, `OPERATION_FAILED`). The intermediate `PROCESSING` status is not published — poll `GET /v1/operations/{operationId}` if you need it. Accounts publish `ACCOUNT_CREATED`, `ACCOUNT_ACTION_REQUIRED`, `ACCOUNT_ASSET_ACTIVATED`, and `ACCOUNT_ASSET_FAILED`. Other account state transitions are not published — poll `GET /v1/accounts/{accountId}` if you need them. # Retry policy Source: https://docs.tracefinance.com/webhooks/retry-policy How Trace Finance handles failed webhook deliveries, retries, and manual replay. ## Overview When your endpoint fails to acknowledge a delivery — non-`2xx` response, network error, or timeout — Trace Finance records the failure and queues a retry. Every attempt is captured in an [execution log](/api-reference/fx-webhook/subscriptions/list-execution-logs) you can inspect or replay manually. ## What counts as success Trace Finance considers a delivery successful when your endpoint returns any `2xx` status code (`200`, `201`, `202`, `204`, etc.). Anything else — `4xx`, `5xx`, network failure, or response timeout — is recorded as `FAILED` and triggers retry. Acknowledge as fast as possible: return `2xx` first, then process the event asynchronously. Long synchronous processing risks timeouts and unnecessary retries. ## Retry behaviour Failed deliveries are queued and re-attempted after a delay. Trace Finance retries the same payload with the same headers and signature; from your side, a retry is indistinguishable from the original delivery — except that `X-Message-Id` stays the same, letting you detect duplicates. | Property | Behaviour | | ------------ | ------------------------------------------------------------------------------------------------------ | | Triggers | Any non-`2xx` response, exception, or network failure | | Backoff | Fixed delay between attempts | | Max attempts | A small number per delivery (typically 2–5) | | Identifier | `X-Message-Id` is constant across retries — use it for idempotency | | Result | After the budget is exhausted, the execution log stays `FAILED` and no further automatic retries occur | Retry counts depend on the deployed environment. Reach out to your Trace Finance contact if you need the current limit for capacity planning. ## Idempotency on your side Always treat handlers as idempotent. The same `X-Message-Id` may arrive multiple times if: * Your endpoint timed out but actually processed the event. * A previous delivery returned `5xx` and is being retried. * You manually [resend a delivery](#manual-replay). Persist `X-Message-Id` somewhere durable (a database, cache, or queue) and short-circuit duplicates before doing any side-effects. ## Inspect execution logs Every delivery attempt is recorded with the request payload, response status, and retry count. ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/subscriptions//executionLogs \ --header 'Authorization: Bearer ' ``` Fetch a single attempt to see the exact body that was sent: ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/subscriptions//executionLogs/ \ --header 'Authorization: Bearer ' ``` ## Manual replay If your endpoint was unavailable during the original retry window, you can replay any execution log on demand. Trace Finance re-sends the same payload, headers, and signature. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/subscriptions//executionLogs//resend \ --header 'Authorization: Bearer ' ``` Returns `204 No Content` if the resend is queued. The replay is recorded as a new attempt in the execution log history. ## Related * [Verify signatures](/webhooks/verify-signatures) — duplicate-safe processing depends on a correctly identified message * [Subscribe to events](/webhooks/subscribe) — pause delivery instead of returning `5xx` when you need a maintenance window * [Resend a delivery](/api-reference/fx-webhook/subscriptions/resend-delivery) — full reference for the manual replay endpoint # Subscribe to events Source: https://docs.tracefinance.com/webhooks/subscribe Register webhook endpoints, scope event types, and manage subscriptions. ## Overview 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 Send a `POST /v1/subscriptions` request with the URL Trace Finance should call and the resources you want to listen to. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/subscriptions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "url": "https://api.example.com/trace-webhooks", "resources": [ { "name": "OPERATION", "includeAll": true } ], "allowRetry": true }' ``` The response returns the subscription with its `id`, the resolved `resources` (each resource with its full event-type list), and `createdAt`. Trace Finance begins delivering matching events immediately. The URL must be **HTTPS** and reachable from the public internet. Self-signed certificates and private networks are not supported. ## Subscribe to every resource Set `includeAll: true` at the top level to receive every event type from every resource. Useful when you have a single handler that fans out internally. ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/subscriptions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "url": "https://api.example.com/trace-webhooks", "includeAll": true, "allowRetry": true }' ``` ## Scope to specific event types To narrow delivery for a resource, pass an `events` array on its `SubscriptionResourceRequest` instead of `includeAll`: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/subscriptions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "url": "https://api.example.com/trace-webhooks", "resources": [ { "name": "BENEFICIARY", "events": ["BENEFICIARY_INSTRUCTION_APPROVED", "BENEFICIARY_INSTRUCTION_REJECTED"] } ], "allowRetry": true }' ``` Each entry in `resources` must have a unique `name`. ## Multiple subscriptions You can register multiple subscriptions — for example, one URL for production and another for staging, or different URLs for different resource subsets. Each subscription is delivered independently; a single event can fan out to several endpoints. There is a per-company limit on active subscriptions. ## List, update, and delete Use the rest of the [Subscriptions API](/api-reference/fx-webhook/subscriptions/list-subscriptions) to manage existing subscriptions: * [`GET /v1/subscriptions`](/api-reference/fx-webhook/subscriptions/list-subscriptions) — list subscriptions for your customer * [`GET /v1/subscriptions/{subscriptionId}`](/api-reference/fx-webhook/subscriptions/get-subscription) — fetch one * [`PATCH /v1/subscriptions/{subscriptionId}`](/api-reference/fx-webhook/subscriptions/update-subscription) — change URL, resources, or `allowRetry` * [`DELETE /v1/subscriptions/{subscriptionId}`](/api-reference/fx-webhook/subscriptions/delete-subscription) — permanently remove ## Update resources or URL Send `PATCH /v1/subscriptions/{subscriptionId}` with only the fields you want to change. To replace the resource list, pass a new `resources` array — it overwrites the existing set. ```bash theme={"theme":"tokyo-night"} curl --request PATCH \ --url https://api.sandbox.tracefinance.com/v1/subscriptions/ \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "resources": [ { "name": "OPERATION", "includeAll": true }, { "name": "BENEFICIARY", "events": ["BENEFICIARY_INSTRUCTION_APPROVED"] } ] }' ``` To stop retrying failed deliveries without removing the subscription, send `{ "allowRetry": false }`. ## Related * [Verify signatures](/webhooks/verify-signatures) — confirm requests came from Trace Finance * [Retry policy](/webhooks/retry-policy) — understand what happens when your endpoint is down * [Test webhooks in sandbox](/webhooks/test-in-sandbox) — drive events for development # Test webhooks in sandbox Source: https://docs.tracefinance.com/webhooks/test-in-sandbox Develop and verify webhook handlers against the Trace Finance sandbox. ## Overview The sandbox environment delivers real webhook events as you exercise the API: opening accounts, registering beneficiaries, initiating PIX payments, and so on. Use it to develop and test your handler before going to production. The sandbox base URL is `https://api.sandbox.tracefinance.com`. To force a specific outcome (a rejection, a failure, a stuck pending state) instead of the default happy path, the sandbox accepts a set of magic values documented in [Testing in sandbox](/guides/testing-in-sandbox). ## Recommended workflow Webhooks must reach a public HTTPS URL. Use the same sandbox or staging environment as the rest of your Trace Finance homologation, the one that calls Trace Finance's sandbox API. Self-signed certificates and private networks are not supported. Point the subscription at your sandbox handler URL and pick the resources you want to test: ```bash theme={"theme":"tokyo-night"} curl --request POST \ --url https://api.sandbox.tracefinance.com/v1/subscriptions \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "url": "https://api.sandbox.example.com/trace-webhooks", "includeAll": true, "allowRetry": true }' ``` Sandbox does not expose a synthetic-event endpoint. To generate events, drive the corresponding API: * **Account events**: complete sandbox account onboarding so an asset activates. * **Beneficiary events**: register a sandbox beneficiary and wait for the compliance review to settle. * **Operation events**: initiate sandbox operations (deposit, withdrawal, swap). Sandbox transactions move through the same state machine as production but with a faster cadence and no real-money settlement, so you typically see the full event lifecycle within seconds. These follow the happy path by default. To drive a rejection, a failure, or a stuck pending state instead, submit the magic values in [Testing in sandbox](/guides/testing-in-sandbox). Use the [execution logs](/api-reference/fx-webhook/subscriptions/list-execution-logs) endpoint to see exactly what was sent and what your handler responded with: ```bash theme={"theme":"tokyo-night"} curl --request GET \ --url https://api.sandbox.tracefinance.com/v1/subscriptions//executionLogs \ --header 'Authorization: Bearer ' ``` Failed attempts can be [resent on demand](/webhooks/retry-policy#manual-replay) once you fix your handler. This replays real payloads against new code without re-running the originating transaction. ## Tips * Keep your sandbox subscription and production subscription in **separate** projects with their own client credentials. Signatures are bound to the client secret, so a sandbox key won't validate production deliveries (and vice versa). * Track which subscription belongs to which environment by URL: point staging at your staging handler and production at your production handler. * If you want to receive sandbox events on a developer laptop for quick exploration, expose the local handler with a public HTTPS tunnel (for example, [ngrok](https://ngrok.com) or [Cloudflare Tunnel](https://www.cloudflare.com/products/tunnel/)) and [PATCH the subscription](/api-reference/fx-webhook/subscriptions/update-subscription) with the tunnel URL. Tunnel URLs typically change between sessions, so update the subscription each time. ## Related * [Environments](/guides/environments) — sandbox and production base URLs * [Testing in sandbox](/guides/testing-in-sandbox) — magic values to force specific sandbox outcomes * [Subscribe to events](/webhooks/subscribe) — full subscription lifecycle * [Retry policy](/webhooks/retry-policy) — replaying past deliveries against updated handlers # Verify signatures Source: https://docs.tracefinance.com/webhooks/verify-signatures Confirm webhook requests came from Trace Finance with HMAC-SHA256 verification. ## Overview Every webhook delivery includes a `X-Message-Signature` header containing an HMAC-SHA256 signature you can verify with your client secret. Verification gives you cryptographic proof that the request originated from Trace Finance and that the message identifier has not been tampered with. Reject any request whose signature does not match — treat unsigned or mis-signed requests as untrusted. ## What is signed The signature is computed over the concatenation of the message ID and your client ID, separated by a `+`: ```text theme={"theme":"tokyo-night"} signature = HMAC-SHA256(secret = clientSecret, data = messageId + "+" + clientId) ``` The output is hex-encoded and sent in the `X-Message-Signature` header. The signature covers the message identifier, not the request body. Body integrity is provided by TLS (HTTPS). Always serve your webhook endpoint over HTTPS. ## Verify the signature Reconstruct the signature on your side using the `X-Message-Id` and `X-Company-Id` headers (or your stored `clientId`) plus your client secret. If the recomputed value matches `X-Message-Signature`, the request is authentic. ```python theme={"theme":"tokyo-night"} import hmac import hashlib def verify_signature(message_id: str, client_id: str, client_secret: str, signature_header: str) -> bool: expected = hmac.new( key=client_secret.encode("utf-8"), msg=f"{message_id}+{client_id}".encode("utf-8"), digestmod=hashlib.sha256, ).hexdigest() return hmac.compare_digest(expected, signature_header) ``` ```javascript theme={"theme":"tokyo-night"} import { createHmac, timingSafeEqual } from "node:crypto"; function verifySignature(messageId, clientId, clientSecret, signatureHeader) { const expected = createHmac("sha256", clientSecret) .update(`${messageId}+${clientId}`) .digest("hex"); return timingSafeEqual( Buffer.from(expected, "hex"), Buffer.from(signatureHeader, "hex"), ); } ``` ```go theme={"theme":"tokyo-night"} import ( "crypto/hmac" "crypto/sha256" "encoding/hex" ) func VerifySignature(messageID, clientID, clientSecret, signatureHeader string) bool { mac := hmac.New(sha256.New, []byte(clientSecret)) mac.Write([]byte(messageID + "+" + clientID)) expected := hex.EncodeToString(mac.Sum(nil)) return hmac.Equal([]byte(expected), []byte(signatureHeader)) } ``` Use a constant-time comparison (`hmac.compare_digest`, `crypto.timingSafeEqual`, `hmac.Equal`) to prevent timing attacks. ## Headers Trace Finance sends | Header | Description | | --------------------- | ------------------------------------------------------------------- | | `X-Message-Id` | Unique UUID per delivery attempt. Use it for idempotent processing. | | `X-Company-Id` | Your Trace Finance company identifier. | | `X-Event-Type` | Event type, e.g., `OPERATION_REQUESTED`. | | `X-Resource-Name` | Resource group, e.g., `OPERATION`. | | `X-Message-Signature` | Hex-encoded HMAC-SHA256 of `messageId+clientId`. | ## Where to find your client secret Your `clientId` and `clientSecret` are issued during onboarding — the same credentials used to obtain access tokens (see [Authentication](/guides/authentication)). Store the secret server-side; never commit it to source control or expose it in client-side code. If you suspect your client secret has been compromised, rotate it immediately and re-verify previously stored event IDs to detect any spoofed deliveries. ## Related * [Retry policy](/webhooks/retry-policy) — handle failed verifications without losing events * [Authentication](/guides/authentication) — how `clientId`/`clientSecret` are provisioned