Changelog
Track API and documentation updates, including new features, behavior changes, and fixes.
2026-10-28
- For organizations where verification links are unavailable,
POST /customers/{customerId}/kycreturns400(A verification link is not available for this customer. Contact support for help.) for AUD-only customers. These customers stay unverified; AUD withdrawals do not need KYC. Standard customers are not affected.
2026-10-27
- AUD withdrawals are available to organizations with AUD functionality enabled; AUD-only customers do not need KYC to be paid out.
POST /customers/{customerId}/withdrawal/banksaccepts AUD withdrawal banks for AUD-only customers when AUD functionality is enabled for your organization (otherwise400):currency: "AUD",paymentRail: "AU_BANK_TRANSFER",bsb(6 digits),accountNumber(1–9 digits, leading zeros kept), the payee'sfirstName,lastNameanddateOfBirth, and the full address. The customer does not need to be KYC verified. The payee is an individual:CUSTOMERandOTHER_INDIVIDUALare active immediately, andOTHER_BUSINESSreturns400. Standard customers get400for AUD withdrawal banks, and AUD-only customers still get400for EUR and USD accounts. It returns502when the account cannot be added right now; nothing is saved, so the same request can be retried unless the error says to contact support. AUD withdrawal banks get their withdrawal wallet with their first withdrawal from the wallet (until thenwalletsis empty); USDC sent to it is paid to the account in AUD at the current rate and fees, like other withdrawal wallets. AUD withdrawal banks reportfundingSource: "VIRTUAL_ACCOUNT": payments to them are sent on behalf of the customer.GET /customers/{customerId}/withdrawal/bankslists an AUD-only customer's AUD withdrawal banks (withbsb,accountNumber,firstNameandlastName) when AUD functionality is enabled for your organization; otherwise the list stays empty. Withdrawal transactions to an AUD withdrawal bank show the samebsb,accountNumber,firstNameandlastNameinwithdrawalBank.POST /customers/{customerId}/withdrawalpays an AUD-only customer's active AUD withdrawal bank when AUD functionality is enabled for your organization (otherwise400), from the wallet (USDC) or a custodial balance (USDC or USDT; EURC returns400). The customer does not need to be KYC verified; a withdrawal bank that is not active returns404. The withdrawal staysPROCESSINGuntil the bank payment settles, then sendswithdrawal.status_changed(COMPLETED, orFAILEDif the bank payment fails or is returned). A withdrawal that reachedCOMPLETEDcan later move toFAILEDif the receiving bank returns the payment, with anotherwithdrawal.status_changed. Standard customers get400for AUD withdrawal banks, and AUD-only customers still get400for EUR and USD accounts. Withdrawal transactions to an AUD withdrawal bank reportpaymentRail: "AU_BANK_TRANSFER". It returns503if the AUD exchange rate is temporarily unavailable; nothing is created, retry shortly.GET /ratesquotesUSDC->AUDandUSDT->AUDonAU_BANK_TRANSFERwhen AUD functionality is enabled for your organization (otherwise400), including for an AUD-onlycustomer_id; a standardcustomer_idreturns400, as AUD withdrawal banks are for AUD-only customers. Any otherpayment_railwith AUD, orAU_BANK_TRANSFERwith another currency, returns400. AUD-only customers still get400for other targets.
2026-10-26
- AUD payments (
paymentRail: "AU_BANK_TRANSFER") fromGET /customers/{customerId}/transactionsand/transactions/{transactionId}now reportfee: the flat AUD fee charged on the payment, null until the payment converts.exchangeRateis now the USDC received per AUD after that fee, rounded to 6 decimal places, sotargetAmount≈ (sourceAmount−fee) ×exchangeRate. Earlier payments report the same fee and rate.developerFeestays null.
2026-10-25
- AUD-only customers.
POST /customersacceptsproduct: "aud_only"for individuals when AUD functionality is enabled for your organization (otherwise403; a corporateaccountTypereturns400).aud_onlyrequires the customer'semail(a missing one returns400). It may be replaced after KYC approval by the email the customer verified.GET /customersandGET /customers/{customerId}returnproduct(standardoraud_only), andbanking.supportedFiatCurrenciesis["AUD"]for AUD-only customers. For AUD-only customers,addressis the residential address they entered during KYC, with any unit leadingaddressLine1, and null for those who get a newkycFlowLinkon every call. GET /customers/{customerId}/deposit?currency=aud(orpaymentRail=AU_BANK_TRANSFER) returns an AUD-only customer's AUD deposit details, which give the BSB and account number, withpaymentRail: "AU_BANK_TRANSFER". It returns409while the customer's AUD account is being set up after KYC approval; poll the deposit endpoint until it stops returning409. It also returns409once the account is closed or being closed (payers should stop sending to it; a payment sent before the closure completes may still arrive), and when the account was never set up and AUD functionality is no longer enabled for your organization (stop polling and contact support). One set of details accepts NPP/Osko and BECS transfers, and the scheme is not reported per transaction. AUD converts to USDC on Base and is sent to your organization's embedded wallet. Requesting AUD for a standard customer returns400. It can return502when AUD deposit details are temporarily unavailable.GET /customers/{customerId}/transactionsand/transactions/{transactionId}return an AUD-only customer's AUD payments asDEPOSITtransactions withpaymentRail: "AU_BANK_TRANSFER"and a newpayerNamefield (the name on the paying bank account). Payments on hold, and returns still in flight, reportPROCESSING; a returned payment reportsREVERSEDonce the return settles, and one returned after it completed keeps itscompletedAt. In the deposit response schema these are theAU_BANK_TRANSFERvariant, wherereference,fee,depositInstructionsanddeveloperFeeare always null, andexchangeRateis the USDC received per AUD, to 4 decimal places.- EUR/USD deposit details, creating or changing virtual accounts, deposit wallets and withdrawal banks, withdrawals, developer fees,
GET /rateswith an AUD-onlycustomer_id,POST /customers/{customerId}/kyc/submit,GET /customers/{customerId}/kyc/addressandPOST /customers/{customerId}/documentsreturn400withNot available for AUD-only customersfor AUD-only customers. Listing an AUD-only customer's virtual accounts, deposit wallets or withdrawal banks returns an empty list, and looking one up returns404. - Some AUD-only customers get a new
kycFlowLinkon every call toPOST /customers/{customerId}/kyc; call it again whenever you send them a link. It returns400once such a customer is verified or their verification is rejected, andGET /customers/{customerId}reports their progress inkyc.stepsStatuswith emptysteps. POST /customers/{customerId}/kycreturns400(Customer is already KYC verified) for an already verified customer with no verification to link to, for organizations that receive a Sumsub WebSDK link, instead of starting a new verification.- AUD-only customers' AUD payments send
deposit.createdanddeposit.status_changedwebhooks like other deposits. - AUD-only customers who get a new
kycFlowLinkon every call send the samecustomer.kyc.submitted,customer.kyc.approvedandcustomer.kyc.rejectedwebhooks as other customers;customer.kyc.submittedis sent when their first KYC link is generated, and again when they continue after being asked for more information. POST /customers/{customerId}/kycreturns400for an AUD-only customer who gets a newkycFlowLinkon every call when the customer is disabled or closed, or has not started verifying while AUD functionality is not enabled for your organization.
2026-10-21
POST /customers/kyc-sharecarries a selfie over only when your Sumsub level records it as a short video (your level's configuration, not each customer's recording). Otherwisereuse.steps.selfieisto_doand the customer records it throughkycFlowLink.POST /customers/kyc-sharenow usually recognises a customer already imported to your organization from the same applicant in your Sumsub account, and returns409 DUPLICATE_CUSTOMERwithexistingCustomerId. If that earlier import isn't finished, the request finishes it instead.POST /customers/kyc-sharedry-run responses includefreshSelfieRequired.
2026-10-18
- Customer withdrawals through this endpoint now require an API key. Dashboard withdrawals continue to use passkey approval.
2026-10-09
- Too many authentication attempts now return
429with aRetry-Afterheader instead of401. Wait the indicated number of seconds and retry; a429does not mean the API key is invalid. All other authentication failures still return401.
2026-10-08
- New
POST /customers/kyc-sharecreates a customer from a Sumsub share token. Verification steps the customer already completed carry over, and the response'skycFlowLinklets the customer finish the rest.idempotencyKeyis required unlessdryRunis set; reuse it on retries so they return the same customer. SetdryRunto check a token without using it. Errors include a machine-readablecode. Returns403when share-token imports are not enabled for your organization. GET /customers/{customerId}now returnskyc.onboardedViaKycShare, true for customers created from a Sumsub share token once the import finished.POST /customers/{customerId}/kyccan return400while that customer's share-token import is still being finished, for organizations using Sumsub-hosted verification links.
2026-10-07
POST /customers/{customerId}/kyccan return a Sumsub-hostedkycFlowLinkfor organizations set up for Sumsub-hosted verification links. That link expires after 7 days; call the endpoint again to get a fresh link for the same verification. For these organizations, a verified customer receives a link to their completed verification, and the endpoint returns400when that is unavailable or when the customer's identity matches an already verified identity.
2026-09-15
GET /deposit-addressesreturns available custodial deposit destinations, with standard checksum formatting for EVM addresses. Use the returned destinations to determine which deposit routes to display.
2026-09-11
GET /deposit-addressescontinues to return available custodial deposit destinations when a self-custody wallet is unavailable.
2026-09-03
- Virtual accounts can now be closed.
GET /customers/{customerId}/virtual-accountandGET /customers/{customerId}/virtual-accountsreturn a new terminal statusCLOSEDwith aclosedAttimestamp; a closed account returns novirtualAccountbank details. Money that still arrives on that IBAN is booked to the same owner. Update any integration that treatsAPPROVEDas final. - New webhook
virtual_account.closed, sent when a customer's virtual account is closed. Payload:virtualAccountId,customerId,currency,status(CLOSED),closedAt. POST /customers/{customerId}/virtual-accountnow returns409when a previous virtual account for that currency was closed. A replacement cannot be requested through the API; contact support.- Shared EUR and USD deposit
accountAddressnow includes the full postal code (V6A 1A4).
2026-08-28
- Custodial EURC balances can now receive supported on-chain EURC deposits and be withdrawn to supported EUR or USD bank accounts.
GET /ratesnow supportsEURC->USDindicative withdrawal quotes.
2026-08-21
- Deposits gain a new terminal status
REVERSED, returned by transaction endpoints and indeposit.status_changedwebhooks when a deposit is returned to the sender's bank account. While a return is in flight the deposit reportsPROCESSING— including on deposits that previously reportedCOMPLETED— before settling atREVERSED. Update any integration that treatsCOMPLETEDas unconditionally final.
2026-08-18
- Custodial stablecoin balances: organizations can now hold USDT (received on Ethereum, Tron or Solana) and USDC (received on Ethereum, Arbitrum or Solana) as a custodial balance, credited when the deposit is allocated by Compose. Received balances are held at the organization level and can be paid out to customers' bank accounts through the withdrawal endpoint below.
POST /customers/{customerId}/withdrawalacceptssourceCurrencyvaluesUSDTandEURCin addition toUSDC, and a new optionalsourcefield (wallet|custodial) to withdraw USDC held on the custodial balance. Custodial withdrawals are debited and paid out immediately (PROCESSING) when custodial balances and API withdrawals are enabled for your organization; they return400for an insufficient balance and403when the feature is not enabled.sourceAmountis now denominated in the chosen source currency.GET /ratesacceptsUSDTas a source currency forUSDT->EURandUSDT->USD, and now also supportsUSDC->USD. USDT rates are withdrawal-only.
2026-08-13
- Customer deposit wallets accept an ownership declaration:
walletType(SELF_CUSTODYorCUSTODIAL) andownershipAttested: true, plusvaspNameand optionalvaspAccountReffor custodial wallets. These fields are optional today and will become required in a future release — start sending them now to avoid a breaking change later. - When you supply
walletType,ownershipAttested: trueis required, and custodial wallets also requirevaspName. Omitting the declaration on an update leaves any existing declaration unchanged, except when you change the wallet address: a declaration covers the address it was made about, so changing the address without supplying a new declaration clearswalletType,vaspName,vaspAccountRefandownershipAttestedAt. Send the declaration alongside the new address to keep the wallet declared. - Wallet responses now include
walletType,vaspName,vaspAccountRefandownershipAttestedAt. They arenullon wallets with no declaration on file. Existing wallets continue to receive deposits without this information.
2026-08-04
- Withdrawal bank create and list responses now include
postalCode. It isnullwhen no postal code is on file for that beneficiary yet. - Creating a withdrawal bank now requires
postalCodewhenever you send an address: always forOTHER_INDIVIDUALandOTHER_BUSINESSrecipients, and forCUSTOMERrecipients if you supplyaddressLine1orcity. Omit those fields to keep resolving the address from the customer's verified address. - Note that
countryis unchanged — it is still resolved from verified data when omitted, and sendingcountryon its own does not require apostalCode.
2026-08-01
GET /customers/{customerId}now returns abankingobject describing what that customer can transact in:supportedFiatCurrenciesand whether a dedicated virtual account is available to them. Use it instead of assuming every customer in your organization supports the same currencies.
2026-07-30
POST /customers/{customerId}/withdrawalnow returns400when the selected withdrawal bank's currency is unavailable for your organization, instead of failing later in processing.POST /customers/{customerId}/virtual-accountnow returns customer-specific403wording when virtual accounts are unavailable for that customer.- Deposit details, rate availability, and withdrawal bank availability now use the banking configuration effective for the requested customer.
2026-07-29
POST /customers/{customerId}/virtual-accountnow returns403when virtual accounts are unavailable for your organization.GET /customers/{customerId}/depositnow returns404when EUR deposit details are unavailable for your organization, matching the existing USD behavior.
2026-07-28
- Corporate customers who request a KYC link are now directed to
[email protected]to complete onboarding.
2026-07-23
- Customer deposit details: EUR/SEPA responses may reference a different receiving bank (for example PayDo EU Ltd. in Malta) depending on your organization's banking configuration. USD deposit details continue to return
404when USD is not available for your organization. GET /rates: requests for a USD rate now return400"USD is not available for your organization" when USD is not offered for your organization.POST /customers/{customerId}/withdrawal/banks: now returns400when a withdrawal bank is created in a currency that is not available for your organization.
2026-07-13
- Withdrawal bank list and create responses now include
fundingSource, indicating whether withdrawals are expected to use a customer virtual account or the Compose shared account.
2026-07-09
- USD FedWire deposit details now include the beneficiary bank BIC in addition to account and routing numbers.
- USD SWIFT deposit details now include the ClearBank IBAN on shared reference account responses.
- USD SWIFT deposit details now identify the ClearBank account with
ibaninstead of also duplicating that value inaccountNumber.
2026-07-07
- Added Wallets and Allowance guides to Getting Started, and clarified that
GET /customers/withdrawal/allowancereturns200with{ "enabled": false, "allowance": null }when API withdrawals are unavailable.
2026-07-03
- USD SWIFT deposit details now return a dedicated
ibananddepositModel: "virtual_account"when the customer has an active USD virtual account; otherwise the shared reference account is returned withdepositModel: "reference".
2026-07-02
expectedMonthlyVolumeon customer create is deprecated for KYC routing: all new customers now start basic KYC and enter their expected volume during the verification flow.- Optional
emailon customer create is deprecated but remains accepted and stored on the customer record. Email for KYC verification is collected during the verification flow.
2026-07-01
POST /customers/{customerId}/documentsnow acceptsDRIVERS(driver's license) andRESIDENCE_PERMIT(residence permit) asidDocTypevalues, in addition toPASSPORT,ID_CARD, andPROOF_OF_ADDRESS.- Verification links returned by
POST /customers/{customerId}/kycare now referred to as "KYC flow links". - Deprecated legacy KYC levels (
customers-flow,customers-ui-flow,customers-ui-flow-enhanced,customers-api-basic,customers-api-enhanced). Existing customers on these levels are unaffected. - Customers with an EU/EEA proof of address now complete an additional EU declaration step, surfaced as an extra questionnaire step in
kyc.steps.
2026-06-29
- When the verification link (
kycFlowLink) is embedded in an iframe, the page now forwards Sumsub verification events to the parent window viawindow.postMessage(idCheck.onApplicantSubmitted,idCheck.onApplicantStatusChanged, andidCheck.onError), so your UI can detect submission, approval, rejection, and errors. See "Detecting events in an iframe" in the integration guide.
2026-06-27
POST /customers/{customerId}/kycnow returns the stable Compose-hosted verification link for hosted customer verification, including customers with an existing applicant record.
2026-06-26
POST /customers/{customerId}/kycnow continues returning a Sumsub WebSDK link for existing customers with an in-progress legacy verification, while new customers receive the hosted Compose verification link when available.
2026-06-19
POST /customers/{customerId}/virtual-accountnow supports corporate customers via API key. Corporate customers must have KYC approved and a complete business address and registration number on file before creation. Contact support if onboarding is incomplete.POST /customers/{customerId}/documentsno longer acceptsDRIVERS(driver's license) orRESIDENCE_PERMIT(residence permit) asidDocTypevalues. The accepted values are nowPASSPORT,ID_CARD, andPROOF_OF_ADDRESS. Requests using the removed types will receive a400response.
2026-06-07
GET /customers/{customerId}now includes anidentityobject withnationality,countryOfResidence, anddateOfBirth(ISOYYYY-MM-DD) for licensed organizations.
2026-06-01
POST /customers/{customerId}/kycnow returns a stable Compose-hosted verification link (https://www.auth.compose.finance/en/signup?c={customerId}) instead of a time-limited Sumsub WebSDK URL.
2026-05-28
- Fiat deposit, withdrawal bank, and transaction payloads are keyed by
paymentRail(SEPA,FEDWIRE,SWIFT) rather than currency alone. Each response variant includes only the bank identifiers for that rail: SEPA usesibanandbic; Fedwire usesaccountNumberandroutingNumber; SWIFT usesaccountNumber,bic, and beneficiary bank details (bankName,bankAddress,bankCountrywhere applicable). - USD SWIFT is supported end to end.
GET /customers/{customerId}/depositcan return SWIFT deposit instructions (account number and BIC). Create USD withdrawal banks withpaymentRail: "SWIFT";bankName,bankAddress, andbankCountryare required. You may pass an IBAN inaccountNumberfor SWIFT banks. POST /customers/{customerId}/withdrawal/banksrequirescurrency(EURorUSD) andpaymentRail. Existing integrations that send onlycurrencycontinue to work and receive the default rail for that currency (EUR → SEPA, USD → FEDWIRE).GET /customers/{customerId}/depositacceptspaymentRailas the primary query parameter. Thecurrencyparameter remains supported and selects the default rail whenpaymentRailis omitted.GET /customers/{customerId}/transactionsandGET /customers/{customerId}/transactions/{transactionId}includepaymentRailon each transaction. For withdrawals, rail-specific bank fields are on the nestedwithdrawalBankobject.GET /ratesaccepts optionalpayment_rail(SEPA,FEDWIRE, orSWIFT) so USD quotes can use Fedwire or SWIFT fee rules. Responses includepayment_railfor the rail used in the quote (your request value or the currency default).
2026-05-24
GET /ratesresponses may now include an optionaldeveloper_feeobject (amount,currency,developerSpreadFeeBps) when quoting a fiat-to-crypto deposit withcustomer_idand the customer has an enabled deposit wallet with a spread fee configured. Platform fees remain infee/fee_currency; quote amounts are unchanged.
2026-05-18
GET /ratesnow accepts an optionaldeposit_modelquery parameter (REFERENCEorVIRTUAL_ACCOUNT, case-insensitive). When omitted, the cheapest available deposit channel is selected automatically so the quote matches the price you will actually pay. Ignored for withdrawals.
2026-05-15
POST /customersnow accepts bothindividualandcorporatevalues foraccountType.- Corporate customers requesting a KYC link now receive guidance that onboarding will be completed by email.
- Added
kyc.rejectionobject toGET /customers/{customerId}response. Populated withrejectLabels[]andreviewRejectType(FINALorRETRY) when the customer is currently in a rejected KYC state;nullotherwise. GET /customers/{customerId}may now include an optionalkyc.rejection.clientCommentstring with a human-readable rejection reason when the verification provider supplied one.
2026-04-29
- Added USD rails across customer deposit and withdrawal APIs.
GET /customers/{customerId}/depositnow supports bothcurrency=eurandcurrency=usd. EUR responses includeiban/bic; USD responses include FedWireaccountNumber/routingNumber.POST /customers/{customerId}/withdrawal/banksnow accepts currency-specific bank details: EUR usesiban/bic, while USD usesaccountNumber/routingNumber.GET /customers/{customerId}/withdrawal/banksreturns the same currency-specific format.- Withdrawal responses from
POST /customers/{customerId}/withdrawal,GET /customers/{customerId}/transactions, andGET /customers/{customerId}/transactions/{transactionId}now returnwithdrawalBankdetails in the correct format for the withdrawal currency (EUR or USD). GET /ratesnow supports USD quotes for theUSD->USDCpair.
2026-03-17
- Added
GET /api/v2/ratesendpoint for indicative exchange rate quotes. Returns a stateless quote for fiat↔crypto currency pairs (EUR ↔ USDC, EURC) including fees. source_currencyandtarget_currencyquery parameters onGET /api/v2/ratesare case-insensitiveexchangeRatedescription on deposit, withdrawal, and rate quote responses now clarifies the formula:targetAmount = sourceAmount × exchangeRate (before fees)
2026-03-12
POST /customers/{customerId}/documentsandPOST /customers/{customerId}/kyc/submitnow return403when manual KYC upload is unavailable for your organization.
2026-03-09
- Added
referencefield to deposit and withdrawal transaction responses. For deposits, this is the inbound bank reference included in the transfer. For withdrawals, this is the reference included in the outbound bank transfer (transaction id).
2026-03-04
- Added
GET /balancesendpoint to retrieve custodial and non-custodial (Safe wallet) balances for your organization, includingavailableBalanceandlockedBalance - Added nullable
addressobject (addressLine1,city,country) toGET /customers/{customerId}response. Returns verified address from KYC or stored metadata when available,nullotherwise. - Added optional
countryfield toPOST /customers/{customerId}/withdrawal/banksrequest body (ISO 3166-1 alpha-2 code). Auto-resolved from KYC/metadata for CUSTOMER recipients; required explicitly for OTHER_INDIVIDUAL and OTHER_BUSINESS. Returns 400 if address cannot be resolved. - Standardized error response descriptions and examples for 401, 403, and 500 across all endpoints
- Renamed
currencyTickertocurrency(lowercased) inGET /developer-feesresponse sourceCurrencyinPOST /customers/{customerId}/withdrawalis now case-insensitiveaccountTypeinPOST /customersis now case-insensitiverecipientTypeinPOST /customers/{customerId}/withdrawal/banksis now case-insensitive
2026-02-25
- Added
addressLine1,city, andcountrytoGET /customers/{customerId}/withdrawal/banksresponses
2026-02-24
- Renamed
tickertocurrencyacross deposit wallet request/response payloads (/customers/{customerId}/deposit/walletsand/customers/{customerId}/deposit/wallets/{walletId}) - Renamed deposit wallet filter query parameter
tickertocurrencyonGET /customers/{customerId}/deposit/wallets - Renamed withdrawal bank wallet field
tickertocurrencyinGET /customers/{customerId}/withdrawal/banksandPOST /customers/{customerId}/withdrawal/banksresponses
2026-02-23
- Added
walletstoGET /customers/{customerId}/withdrawal/banksandPOST /customers/{customerId}/withdrawal/banksresponses POST /customers/{customerId}/withdrawal/banksnow auto-creates a linked withdrawal wallet and returns it when successful
2026-02-20
- Added
GET /customers/{customerId}/kyc/addressendpoint to retrieve a customer's verified address - Made
addressLine1andcityoptional forCUSTOMERrecipient type — address is auto-resolved from KYC data when not provided - Changed
recipientTypeenum values fromMYSELF/INDIVIDUAL/BUSINESStoCUSTOMER/OTHER_INDIVIDUAL/OTHER_BUSINESS
2026-02-19
- Added
isThirdPartyfield to withdrawal bank responses - Third-party withdrawal banks now require approval (
PENDING_APPROVALstatus)
Webhooks
Previous Page
Get balances GET
Returns custodial (account) and non-custodial (Safe wallet) balances for your organization. **Custodial balances** are grouped by asset and summed across chains. These represent funds held on your behalf by Compose. **Non-custodial balances** are queried on-chain from your organization's Safe wallet. Each entry includes the chain and token contract address. Only non-zero balances are returned; amounts are decimal strings without trailing zeros. `nonCustodial.address` is an empty string when your organization has no Safe wallet.

