ChangelogRelease History

Changelog

API changes, new capabilities, and anything you might need to act on.

Additive changes, such as new fields, endpoints, and event kinds, ship without notice, so build your parsers to ignore unknown fields. A breaking change gets an entry here with a migration note before it reaches live, and rides the Api-Version header.

2026-10-21: Strict amounts, consistent names, dashboard onboarding

These changes land in API version 2026-08-01. We are not live yet, so several are breaking. Each carries its migration.

Amounts

  • Responses use plain decimal strings without trailing zeros. "7", "0.5", "0", never "7.000000000000000000" or "0E-18". The same value reads the same in every response and webhook. Migration: compare amounts as decimals, not strings.
  • Send amounts as strings. A fractional JSON number is refused with 422 invalid_parameter ("send amounts as strings"); JSON integers are accepted. A request amount must be positive, with at most 38 digits and 18 decimal places. An amount finer than the asset's decimals is 400 invalid_amount, now on a quote estimate's target_amount too.
  • Fee and rate precision. total_cost_bps is a decimal string and may be fractional. exchange_rate carries 12 decimal places, and the delivery is priced with exactly that rate. Deliveries round down to the target asset's decimals.

Renamed fields, events, and codes

  • Deposit rail is payment_rail, and deposit asset is currency. Corridor source.asset and source.rail are source.currency and source.payment_rail. The sandbox deposit body's rail is payment_rail.
  • A wallet operation's address is destination_address everywhere: the request (was to_address), the operation (was destination), and the signing contract's authorization.intent (was destination, and its operation is now kind). Update your verifier's comparison.
  • Operation status confirmed is completed.
  • Event kinds are <object>.<what happened>: wallet.operation_confirmed and wallet.operation_failed are wallet_operation.completed and wallet_operation.failed; wallet.deposit_received and wallet.withdrawal_created are one wallet_transaction.created (the object's kind says deposit or withdrawal); wallet.deposit_returned is wallet_transaction.returned; tos.accepted is tos_acceptance.created. customer.created is now sent.
  • Quote as_of is quoted_at.
  • Error codes: public_keys_required is wallet_keys_required; partner_tos_required is partner_terms_required; operation_not_found is 404 not_found_error wallet_operation_not_found; virtual_account_exists is now 409; invalid_value is gone (it is invalid_parameter), and invalid_parameter is always 422.
  • Path parameters are named for their resource: {virtual_account_id}, {webhook_event_id}.

Webhooks

  • The signing secret comes from POST /api/webhooks, once, in its secret field. GET /api/webhooks/portal is removed.
  • Subscribing to every kind reads back as event_kinds: null (was ["*"]). An unknown kind in event_kinds is 422 invalid_parameter, and an empty list is refused.
  • Replaying an event delivers it every time you call replay.

Stricter requests

  • Customer create and update refuse unknown fields with 422 invalid_parameter, instead of ignoring them.
  • A document file that is not valid base64 is 422 invalid_parameter.
  • Unknown routes, wrong methods and over-size bodies (128 MiB) answer with the standard error envelope: route_not_found, method_not_allowed, request_too_large.
  • A Solana destination_address that is not a valid address is 422 invalid_parameter.

Behavior

  • A revoked or expired API key never receives a stored idempotent response: 401 api_key_revoked or 401 api_key_expired. Idempotency records last 24 hours, after which a reused key runs as new.
  • The wallet balance check counts operations already in flight (executing, or awaiting authorization and not yet expired).
  • Rate limits apply per API key once it has authenticated, and per client IP otherwise.
  • Conversions carry network. Every list carries {data, has_more, next_cursor}, including balances and corridors.
  • deposit.network_reference is the bank's trace reference when the bank provides one, and null otherwise.
  • Requirement code unavailable is retried automatically; you need not act.

Onboarding moves to the dashboard

API keys, wallet-key registration and activation, and your own onboarding (Partner Terms, terms acceptance, KYB, wallet keys) are all done in the dashboard. See Get set up.

2026-10-14: Every required document, asked up front

These changes land in API version 2026-08-01. One changes what a business owes before its submit; the rest are additive.

Behavior change

  • A business owes every document its legal form needs before the submit. Until now a business owed only its registration document and each controller's documents up front, and the review asked for the rest afterwards. Now the endorsement's missing bucket lists them all, by business_kind, and the submit returns 422 verification_incomplete while any is owed. A business's own proof_of_address is among them, so one we cannot accept now stops the submit, and a sole proprietorship owes a proof_of_tax_identification. Migration: collect the documents in the table before you submit, and send business_kind on create so missing lists them from the start. See Documents we ask for.

Added

  • New document purposes and requirement codes: articles_of_association (articles of incorporation or organization, bylaws, or a memorandum of association), operating_agreement (an LLC operating agreement, or a partnership or limited-partnership agreement), and proof_of_signatory_authority (the list of the people authorized to represent or sign for the business). Send them in documents on create or PATCH.
  • A US resident no longer owes a proof of address. An individual owes one when they live outside the US, or where they live is not known yet. A US resident may still send one, and we read it.

2026-10-14: Businesses read from their documents, and controller ids

These changes land in API version 2026-08-01. One changes what a submit can refuse; the rest are additive.

Behavior change

  • A document we cannot accept now stops the submit. Until now only a document that could not be read was refused. Reading now also checks each document: one that has expired, a proof of address older than we accept, one that carries a different name than the one we hold, or one that fails its authenticity checks moves its requirement code to issues, and the submit returns 422 verification_incomplete naming the document and the reason before anything is sent to review. After the first submit, a PATCH that resubmits sends nothing until the document is replaced. Migration: on that 422, send a new document with a PATCH, then submit. See A document we cannot accept is asked for again.

Added

  • Businesses are read from their documents, like individuals. The registration document fills legal_name, business_kind, registration_number, incorporation_date, and the registered address. The business's own proof of address fills physical_address. Each controller's documents fill that person's names, date of birth, nationality, identity document number, address, and tax data. A value you send is never overwritten, and missing lists only what the documents could not supply. See Documents are read at submit.
  • legal_name is optional when you create a business, and so are a controller's first_name and last_name.
  • The identity document's kind and issuing_country are optional, on identifying_information and, for kind, on id_document. We read both from the image and ask for them only when they cannot be read: as issuing_country, or as the new requirement code id_document_kind.
  • New document purpose proof_of_tax_identification, on the customer and on each controller: a tax certificate or letter showing the taxpayer identification number. It fills tax_id and tax_residence_country. It is optional, and its codes, proof_of_tax_identification and controller_proof_of_tax_identification, appear in the buckets only once you send one.
  • Controllers have ids. A business customer now lists controllers, each with an id (ctl_...), the names, and the role flags, and never identity data. Send the id with a person on an update of controllers to keep them and the documents held for them. A person without one is matched on first name, last name, and date of birth, else is new. An id that is not a person on the customer returns 422 invalid_parameter. See People on a business.
  • New customer fields, all optional: middle_name on an individual and on a controller, and trade_name and business_description on a business. Responses carry middle_name for an individual and trade_name and business_description for a business.
  • New requirement code controller_name: every controller's first and last name, listed when neither you nor their identity document supplied them.

2026-10-10: Intake method and tax data at review

These changes land in API version 2026-08-01, and none of them requires a change to an existing integration.

  • intake_method on the submit. POST /api/customers/{customer_id}/verifications takes an optional intake_method. api, the default, submits everything you sent through POST and PATCH /api/customers, exactly as before, so an empty body behaves as it did. hosted is coming: one standing link per customer, valid for 90 days, that collects everything still owed, terms included. Until it opens, intake_method: "hosted" returns 400 hosted_intake_unavailable (actionable), ahead of the submit's other refusals. redirect_url is reserved for it: sent without intake_method: "hosted", it returns 422 invalid_parameter. See Verification & Endorsements.
  • Three new fields on the verification round. intake_method is api on every round you can create today. url and expires_at are reserved for the standing link and are always null today.
  • Tax data is no longer needed to request endo_usd. Requesting it now needs email and phone for an individual, and email for a business, so 422 endorsement_profile_required names only those. The review still needs tax_id, and tax_residence_country for an individual. In live, until you send them, the endorsement lists them in missing and the submit's 422 verification_incomplete names them. Sending them on create, as before, keeps the submit unblocked.

2026-10-09: Verification by endorsement

Each endorsement is now the review of the customer for its currency, and that review is the whole verification. These changes land in API version 2026-08-01 before live launch, so they carry no new version date. If you built against the sandbox, apply the migration notes below.

Breaking

  • The submit returns a list. POST /api/customers/{customer_id}/verifications returns {"data": [...], "has_more": false, "next_cursor": null}, with one round per requested endorsement, and each round names its endorsement. Migration: read the rounds from data.
  • requested_info is no longer accepted on the submit. The body is empty, and any field returns 422 invalid_parameter. requested_info stays on the round as output: the asks that round answers. Migration: send {}.
  • verification_required is replaced by the endorsement errors. Creating a virtual account returns 400 endorsement_required when the currency's endorsement was never requested, and 400 endorsement_not_approved when it is not approved. Creating a wallet returns 400 endorsement_required until an endorsement is approved. Migration: branch on those codes.
  • The aml requirement code is removed, and so is customer_kind_unsupported. document_rejected is no longer returned.
  • A PATCH resubmits only when something changed. After the first submit, a new document, a corrected value, or a newly requested endorsement resubmits by itself. The same file or value again does nothing. Migration: stop calling the submit after each fix.

Also changed

  • Documents are read at submit. For an individual, the newest identity document and proof of address fill the fields you left empty. A value you sent is never overwritten. See Verification & Endorsements.
  • New errors. 503 document_reading_unavailable (retryable, nothing submitted) and 409 verification_not_needed on the submit, plus 400 endorsement_required when no endorsement is requested. The sandbox decision route adds 400 verification_not_under_review. See Errors.
  • The review can ask for more after approving. An approved endorsement moves to issues with endorsement.request_for_information. When we cannot reach the review, the endorsement shows issues with unavailable, no webhook fires, and we retry automatically.
  • Business requirement codes. ownership_information, controllers, the controller_* codes, and the company fields such as legal_name and physical_address join the code catalog.
  • Sandbox reviews stay pending until decided. The decision applies to the latest submitted review, and documents read as a fixed sample person. POST /api/sandbox/wallets/{wallet_id}/deposits is removed: fund sandbox wallets from a testnet faucet. See Sandbox Testing.

2026-09-05: network beside chain

Every chain-bearing object (wallets, balances, operations, payouts, transactions, corridors, quote targets, and virtual-account destinations) carries network: testnet in sandbox, mainnet in live. chain stays the same in both, so your integration never branches on it. The operation's former network field, which named the chain, is now chain.

Initial release: API version 2026-08-01

  • Onboarding: customer terms acceptance through a hosted link or a programmatic attestation. Customers, individuals and businesses, with documents shared through the API. Verification is submitted explicitly, and USD access is requested as the endo_usd endorsement.
  • Virtual accounts: a dedicated USD account per customer, received over ACH and Fedwire, with a standing stablecoin destination and a fee you set per account. See Virtual Accounts.
  • Money movement: deposits convert and deliver automatically as conversions and payouts, with itemized fees and the exact exchange rate. A unified transactions feed, balances, quote estimates, and corridor discovery. See Money Movement.
  • Wallets: non-custodial customer wallets on Solana. Every send is an operation you verify and authorize with your own key. See Wallets and Sending Funds.
  • Coverage: USDC and USDT delivered on Solana. See Coverage.
  • Platform: Api-Key authentication with separate sandbox and live hosts, required idempotency keys on POST, one error envelope, cursor pagination, and signed webhooks with replay.
  • Sandbox: simulated verification decisions, treasury funding, and fiat deposits, with wallets on testnet. See Sandbox Testing.
Was this page helpful?