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 is400 invalid_amount, now on a quote estimate'starget_amounttoo. - Fee and rate precision.
total_cost_bpsis a decimal string and may be fractional.exchange_ratecarries 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
railispayment_rail, and depositassetiscurrency. Corridorsource.assetandsource.railaresource.currencyandsource.payment_rail. The sandbox deposit body'srailispayment_rail. - A wallet operation's address is
destination_addresseverywhere: the request (wasto_address), the operation (wasdestination), and the signing contract'sauthorization.intent(wasdestination, and itsoperationis nowkind). Update your verifier's comparison. - Operation status
confirmediscompleted. - Event kinds are
<object>.<what happened>:wallet.operation_confirmedandwallet.operation_failedarewallet_operation.completedandwallet_operation.failed;wallet.deposit_receivedandwallet.withdrawal_createdare onewallet_transaction.created(the object'skindsaysdepositorwithdrawal);wallet.deposit_returnediswallet_transaction.returned;tos.acceptedistos_acceptance.created.customer.createdis now sent. - Quote
as_ofisquoted_at. - Error codes:
public_keys_requirediswallet_keys_required;partner_tos_requiredispartner_terms_required;operation_not_foundis404 not_found_errorwallet_operation_not_found;virtual_account_existsis now409;invalid_valueis gone (it isinvalid_parameter), andinvalid_parameteris always422. - Path parameters are named for their resource:
{virtual_account_id},{webhook_event_id}.
Webhooks
- The signing secret comes from
POST /api/webhooks, once, in itssecretfield.GET /api/webhooks/portalis removed. - Subscribing to every kind reads back as
event_kinds: null(was["*"]). An unknown kind inevent_kindsis422 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_addressthat is not a valid address is422 invalid_parameter.
Behavior
- A revoked or expired API key never receives a stored idempotent
response:
401 api_key_revokedor401 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_referenceis the bank's trace reference when the bank provides one, andnullotherwise.- Requirement code
unavailableis 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
missingbucket lists them all, bybusiness_kind, and the submit returns422 verification_incompletewhile any is owed. A business's ownproof_of_addressis among them, so one we cannot accept now stops the submit, and a sole proprietorship owes aproof_of_tax_identification. Migration: collect the documents in the table before you submit, and sendbusiness_kindon create somissinglists 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), andproof_of_signatory_authority(the list of the people authorized to represent or sign for the business). Send them indocumentson 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 returns422 verification_incompletenaming 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 registeredaddress. The business's own proof of address fillsphysical_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, andmissinglists only what the documents could not supply. See Documents are read at submit. legal_nameis optional when you create a business, and so are a controller'sfirst_nameandlast_name.- The identity document's
kindandissuing_countryare optional, onidentifying_informationand, forkind, onid_document. We read both from the image and ask for them only when they cannot be read: asissuing_country, or as the new requirement codeid_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 fillstax_idandtax_residence_country. It is optional, and its codes,proof_of_tax_identificationandcontroller_proof_of_tax_identification, appear in the buckets only once you send one. - Controllers have ids. A business customer now lists
controllers, each with anid(ctl_...), the names, and the role flags, and never identity data. Send theidwith a person on an update ofcontrollersto 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. Anidthat is not a person on the customer returns422 invalid_parameter. See People on a business. - New customer fields, all optional:
middle_nameon an individual and on a controller, andtrade_nameandbusiness_descriptionon a business. Responses carrymiddle_namefor an individual andtrade_nameandbusiness_descriptionfor 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_methodon the submit.POST /api/customers/{customer_id}/verificationstakes an optionalintake_method.api, the default, submits everything you sent throughPOSTandPATCH /api/customers, exactly as before, so an empty body behaves as it did.hostedis coming: one standing link per customer, valid for 90 days, that collects everything still owed, terms included. Until it opens,intake_method: "hosted"returns400 hosted_intake_unavailable(actionable), ahead of the submit's other refusals.redirect_urlis reserved for it: sent withoutintake_method: "hosted", it returns422 invalid_parameter. See Verification & Endorsements.- Three new fields on the verification round.
intake_methodisapion every round you can create today.urlandexpires_atare reserved for the standing link and are alwaysnulltoday. - Tax data is no longer needed to request
endo_usd. Requesting it now needsemailandphonefor an individual, andemailfor a business, so422 endorsement_profile_requirednames only those. The review still needstax_id, andtax_residence_countryfor an individual. In live, until you send them, the endorsement lists them inmissingand the submit's422 verification_incompletenames 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}/verificationsreturns{"data": [...], "has_more": false, "next_cursor": null}, with one round per requested endorsement, and each round names itsendorsement. Migration: read the rounds fromdata. requested_infois no longer accepted on the submit. The body is empty, and any field returns422 invalid_parameter.requested_infostays on the round as output: the asks that round answers. Migration: send{}.verification_requiredis replaced by the endorsement errors. Creating a virtual account returns400 endorsement_requiredwhen the currency's endorsement was never requested, and400 endorsement_not_approvedwhen it is not approved. Creating a wallet returns400 endorsement_requireduntil an endorsement is approved. Migration: branch on those codes.- The
amlrequirement code is removed, and so iscustomer_kind_unsupported.document_rejectedis 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) and409 verification_not_neededon the submit, plus400 endorsement_requiredwhen no endorsement is requested. The sandbox decision route adds400 verification_not_under_review. See Errors. - The review can ask for more after approving. An approved endorsement
moves to
issueswithendorsement.request_for_information. When we cannot reach the review, the endorsement showsissueswithunavailable, no webhook fires, and we retry automatically. - Business requirement codes.
ownership_information,controllers, thecontroller_*codes, and the company fields such aslegal_nameandphysical_addressjoin the code catalog. - Sandbox reviews stay
pendinguntil decided. The decision applies to the latest submitted review, and documents read as a fixed sample person.POST /api/sandbox/wallets/{wallet_id}/depositsis 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_usdendorsement. - 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-Keyauthentication 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.