Route reference
Every route below is served identically by https://api.sandbox.cashxchain.com
and https://api.cashxchain.com, except where marked sandbox only. Guides
explain the behaviour; this page is the index of the contract.
Conventions
- JSON in and out. Send
Content-Type: application/jsonwith a body. - Amounts are decimal strings — send
"10.00", read"10.0000". Never floats. - Currencies are ISO 4217 (
EUR), countries ISO 3166-1 alpha-2 (DE). - Timestamps are RFC 3339, UTC.
- Ids: payouts and conversions have UUIDs — a conversion's is listed in
GET /v1/banking/payouts, sincefx/executeanswers with the rail'sdeal_id. Accounts, beneficiaries, quotes and KYB applications have ids issued by the rail — opaque strings whose shape differs between sandbox and production. - Errors use one envelope; framework-level rejections are plain text — see Errors.
- Every response carries
X-Request-Id.
Credentials in the tables: S dashboard session · M master key
(mk_) · K secret key (sk_) · R restricted key (rk_) · P
publishable key (pk_). R needs the scope shown; K holds every scope; S and
M are not subject to key scopes, though S is subject to its seat's
permissions. See
Authentication.
Headers: Idem = Idempotency-Key required · Step-up =
X-Step-Up-Token required of sessions · Delegable = accepts
Cxc-Account with the grant scope shown; money marks the
routes that need a session or a master key with the money permission when
delegated.
Money API — /v1/banking/*
Credentials for every route in this section: S, M, K, and R with the
scope shown. pk_ keys get 401 key_not_admitted. A partner query or body
field, which older clients used to pin a rail, is 400 bad_request on every
route here.
Accounts
GET /v1/banking/accounts
Your accounts, currency accounts, plan usage and the currencies you can open.
200{accounts[], refreshing, primary_currency, currency_accounts[], plan, offered_currencies[], degraded_partners?[], closing_accounts[]}.- Errors:
503 service_unavailable(no rail answered). - Delegable:
accounts:read. → Accounts
POST /v1/banking/accounts
Open a currency account. Idem.
- Body
{currency}. 201{currency_account: {currency, kind, status, is_primary, created_at}, account?}—status: "requested"and noaccountwhen the rail opens it later.- Errors:
400 bad_request,403 currency_limit_reached,403 named_account_limit_reached,409 conflict. - Delegable:
accounts:write, money. → Accounts
PUT /v1/banking/accounts/primary
Set the primary currency.
- Body
{currency}.200{primary_currency}. - Errors:
422 primary_not_eligible. - Delegable:
accounts:write.
POST /v1/banking/accounts/close
Close a currency account, converting any balance to the primary currency.
- Body
{currency}.200{currency, status: closed|closing, step, convert_to}. - Errors:
404 not_found,422 primary_account, lockdown codes. - Not delegable.
GET /v1/banking/accounts/plan-impact
Which accounts a plan change would close.
- Query:
plan(required:free,starter,professional,business,enterprise). 200{plan, max_named_accounts, max_currencies, accounts_to_close[{currency, kind, balance, convert_to}]}.- Errors:
400 bad_request. Delegable:accounts:read.
GET /v1/banking/accounts/{account_id}/payin-instructions
Bank details for paying into the account.
200{instructions[{rail, currency, account_holder_name, identifier, bank_name?, bank_address?, reference?}]}.- Errors:
404 not_found. Delegable:accounts:read. → Accounts
GET /v1/banking/accounts/{account_id}/balance
200{balances[{account_id, currency, available, pending, as_of}], refreshing}.- Errors:
404 not_found. Delegable:accounts:read. → Accounts
POST /v1/banking/accounts/{account_id}/statements
Issue a statement.
- Body
{from, to}(dates, ≤ 366 days, not in the future). 201statement withstatement_id, period,account,customer, balances, totals,lines[],checksum,sandbox.- Errors:
400 bad_request,404 not_found,503 service_unavailable. Not delegable. → Accounts
Beneficiaries
GET /v1/banking/corridors
What a destination needs.
- Query:
currency,country(required);entity_type(business/individual),payment_method(bank_account/e_transfer). 200{currency, country_code, accepted_schemes[], rails[], fields[], payment_method?, payment_methods[], entity_type?, rail_field?, reference_max_length?, payment_purposes[]}.- Errors:
400 bad_request,422 corridor_unsupported,503 service_unavailable. Scopeaccounts:read; delegable. → Beneficiaries
GET /v1/banking/beneficiaries
200{beneficiaries[], degraded_partners?[]}.- Scope
accounts:read; delegable.
POST /v1/banking/beneficiaries
Register a destination. Idem.
- Body
{beneficiary_type, name, currency, country_code, account: {scheme, …}, bank_name?, address?, corridor_fields?}. 201{id, beneficiary_type, name, currency, country_code, account, bank_name?, usable_at?};200for an existingcxc_handlepayee.- Errors:
400 bad_request,404 username_not_found,409 conflict,422 corridor_unsupported,503 service_unavailable, lockdown codes. - Scope
beneficiaries:write; delegable, money. → Beneficiaries
POST /v1/banking/beneficiaries/{beneficiary_id}/verify
Run the payee-name check.
200{outcome: matched|close_match|no_match|not_checked|invalid, may_pay, account_holder?, check?}.- Errors:
404 not_found. Scopebeneficiaries:write; not delegable. → Beneficiaries
POST /v1/banking/beneficiaries/{beneficiary_id}/accept-verification
Record that the payer saw the bank's verdict and chose to pay — scoped to one payment.
- Body
{payment_idempotency_key}. 200{outcome, accepted, verification_id?, acceptance_id?, account_holder?, partner_acknowledged}.- Errors:
400 bad_request,404 not_found,422 payee_name_mismatch(aninvaliddestination). Scopebeneficiaries:write; not delegable. → Beneficiaries
Payouts
POST /v1/banking/payouts
Send a payout. Idem. Step-up.
- Body
{source_account_id, beneficiary_id, amount, currency, reference?, fx_quote_id?, purpose_of_payment?, rail?, destination_country?, quote_id?}— or, instead ofbeneficiary_id,destination_account_id(one of your own accounts) ordestinationwithsource_network/destination_network(a send from a digital-asset account, sandbox). 202payout{id, status, partner_payment_id?, amount, currency, direction, rail?, partner_status?, created_at, updated_at, submitted_at?, settled_at?, source_account_id, destination_account_id, arrival, quote_id?, route?, kind?, network?, destination_network?, destination_currency?, tx_hash?};200on a replay.- Errors:
400 bad_request,400 insufficient_balance,403 account_not_verified,403 permission_denied,404 not_found,409 idempotency_key_reuse,409 quote_expired,409 quote_unrecorded,409 route_changed,409 route_quote_used,422 corridor_unsupported,422 payee_name_mismatch,422 plan_limit_exceeded,422 no_rate,422 route_quote_expired,422 route_quote_mismatch,503 limit_unmeasurable,503 fee_unpriceable,503 service_unavailable, step-up and lockdown codes. From a digital-asset account, its own codes — Digital-asset errors. - Scope
payments:write; delegable, money. → Payouts, Sending from a digital-asset account
GET /v1/banking/payouts
- Query:
limit(1–200, default 50),before(the previous page'snext_cursor). 200{payouts[], has_more, next_cursor}— newest first, conversions included.has_moreistrueexactly whennext_cursoris set; on the last pagenext_cursorisnull.- Delegable:
payments:read. → Payouts
GET /v1/banking/payouts/{payout_id}
200payout. Errors:404 not_found,400(plain text) for a non-UUID id.- Delegable:
payments:read. → Payouts
POST /v1/banking/payouts/quote
Price a payout, and disclose and lock its route. Moves nothing.
- Body
{source_account_id, currency, amount, beneficiary_id?, rail?, destination_country?}— or the payout's other destinations, as forPOST /v1/banking/payouts. 200{source_currency, source_amount, fee, payment_fee, rate, currency, amount, simulated, rate_source?, quote_id, expires_at, route, rate_locked}(quote_idnullwithout a destination); a digital-asset send answers its own estimate — Quotes.- Errors:
404 not_found,422 no_rate,503 fee_unpriceable,503 service_unavailable. Delegable:payments:read. → Payouts
GET /v1/banking/capabilities
What this deployment offers and what your account may do now.
200{environment, flows[], digital_assets[], digital_rules, payout_rails[], route_lock, account, caller, actions[]}.- Scope
accounts:read. → Capabilities
GET /v1/banking/payout-options
200{currencies[], countries[{country_code, default_currency}]}. Delegable:payments:read.
FX
POST /v1/banking/fx/quote
- Body
{sell_currency, buy_currency, amount, fixed_side?: sell|buy}. 200{quote_id, sell_currency, buy_currency, sell_amount, buy_amount, rate, tier, unit_fee?, unit_fee_currency?, expires_at, tradable?, rate_source: live|reference}.- Errors:
400 bad_request,422 corridor_unsupported,422 market_closed,503 service_unavailable. Scopefx:read; delegable. → FX
POST /v1/banking/fx/execute
Convert between your accounts. Idem. Step-up for sessions from 10,000 USD.
- Body
{quote_id, account_id}. 200{deal_id, status, raw}.- Errors:
400 account_currency_mismatch,400 quote_expired,400 quote_consumed,400 insufficient_balance,400 invalid_amount,400 bad_request,403 account_not_verified,403 permission_denied,409 conflict,409 idempotency_key_reuse,409 quote_unrecorded, step-up and lockdown codes. - Scope
payments:write; delegable, money. → FX
Verification (KYB)
None takes an Idempotency-Key. All are delegable — reads with
accounts:read, writes with accounts:write. → KYB
| Route | Body | Success | Errors |
|---|---|---|---|
POST /v1/banking/kyb/precheck/preview | {legal_name, country_code, registration_number?, industry_code?, business_description?} | 200 {blocking_findings[]} | — |
POST /v1/banking/kyb/precheck | application | 200 {precheck_record_id, decision, findings[], existence[], sanctions_hits[], unavailable_sources?, missing_onboarding_fields?} | 400 |
GET /v1/banking/kyb/precheck-records | query limit (1–100, default 20) | 200 {records[]} | — |
POST /v1/banking/kyb/referrals | application | 201 referred · 202 manual_review · 200 already_referred — {outcome, referral?, precheck?, message?} | 400, 422 kyb_precheck_blocked, 503 |
GET /v1/banking/kyb/referrals/{external_id} | — | 200 {external_id, status, raw} | 404 |
POST /v1/banking/kyb/referrals/{external_id}/persons | person | 201 {person_external_id} | 400, 404 |
POST /v1/banking/kyb/referrals/{external_id}/documents/slots | {document_type, content_type, other_document_type?} | 200 partner slot payload | 400, 404 |
POST /v1/banking/kyb/referrals/{external_id}/persons/{person_external_id}/documents/slots | {id_type, id_number, issued_country, id_expiry_date?, address_type, id_content_type, address_content_type, document_name?} | 200 partner slot payload | 400, 404 |
POST /v1/banking/kyb/referrals/{external_id}/parent-entities | {role, entity_category, legal_name, registration_id, date_of_incorporation, email, website?, trading_name?, address, owners[]} | 201 {parent_entity_external_id, owner_external_ids[]} | 400, 404 |
POST /v1/banking/kyb/referrals/{external_id}/parent-entities/{parent_entity_external_id}/documents/slots | {document_type, content_type} | 200 partner slot payload | 400, 404 |
POST /v1/banking/kyb/referrals/{external_id}/finalise | — | 200 {external_id, status: "submitted"} | 400, 404 |
Settlement
Automatic conversion of incoming money into another currency.
| Route | Body / query | Success | Notes |
|---|---|---|---|
GET /v1/banking/settlement/policies | — | 200 {policies[{source_currency, target_currency, enabled, min_amount, updated_at}]} | Delegable: accounts:read |
PUT /v1/banking/settlement/policies | {source_currency, target_currency, enabled?, min_amount?} | 200 the policy | 400; lockdown codes; not delegable |
GET /v1/banking/settlement/intents | limit (1–200, default 50) | 200 {intents[]} — status: pending, quoted, settled, failed, needs_reconciliation, no_rail | Delegable: accounts:read |
Developer API
Rate-limited at 600 requests a minute per key (per account for sessions), counted per server. → Rate limits
FX rates
GET /v1/fx/rates
Indicative rates for display — not a quote.
- Credentials: S, M, K, R, P · scope
fx:read. - Query:
base,currencies(comma-separated, ≤ 30). 200{base, rates[{currency, rate, rate_source: live|reference}], unavailable[], as_of}—rateis units ofbaseper unit ofcurrency.- Errors:
400 bad_request,403 insufficient_scope. Delegable:fx:read. → FX
Webhooks
Credentials: S, M, K, R, P (P on GET only). → Webhooks
| Route | Scope | Request | Success | Errors |
|---|---|---|---|---|
GET /v1/webhooks | accounts:read | — | 200 {object: "list", data[{id, object, url, description, events, status, success_rate, disabled_reason, created_at}]} | — |
POST /v1/webhooks | accounts:write | {url, description?, events?[]} | 201 endpoint with secret | 400, 409 conflict |
GET /v1/webhooks/events | accounts:read | — | 200 {object: "list", data[{name, description}]} | — |
GET /v1/webhooks/deliveries | accounts:read | endpoint_id?, limit (1–100, default 20) | 200 {object: "list", data[{id, object, endpoint_id, event, event_id, status, http_status?, attempts, latency_ms?, last_error?, created_at, delivered_at?}]} | — |
DELETE /v1/webhooks/{id} | accounts:write | — | 204 | 404 |
POST /v1/webhooks/{id}/rotate-secret | accounts:write | — | 200 endpoint with new secret | 404 |
API keys
Credentials: S (a seat holding api_keys.manage: owner, admin, personal
account) and M only — any API key gets 403 key_not_admitted, a member or
viewer session 403 permission_denied. Not delegable.
→ Managing API keys
| Route | Request | Success | Errors |
|---|---|---|---|
GET /v1/api-keys | — | 200 [{id, object, name, key_id, prefix, last4, legacy, type, scopes, last_used_at, created_at}] | — |
POST /v1/api-keys | {name, type?, scopes?[]} | 201 key with token, shown once | 400, 403 permission_denied (a key beyond the creator's seat) |
DELETE /v1/api-keys/{id} | — | 204 | 404 |
Request logs and usage
Credentials: S, M, K — rk_/pk_ get 403 key_not_admitted. Not delegable.
→ Request IDs and logs
| Route | Query | Success |
|---|---|---|
GET /v1/request-logs | limit (1–500, default 100), before | 200 {logs[{id, method, path, status_code, duration_ms, api_key_id?, ip_address?, idempotency_key?, created_at}], has_more, next_cursor?} |
GET /v1/request-logs/{id} | — | 200 the entry plus request_body, response_body; 404 |
GET /v1/usage/summary | — | 200 {requests_7d, requests_7d_delta_pct, error_rate_pct, total_volume, avg_settlement_secs} |
GET /v1/usage/timeseries | window: 7d, 30d, 90d | 200 {window, data[{date, count}]} |
Account and platform reads
| Route | Credentials · scope | Success | Notes |
|---|---|---|---|
GET /v1/meta/scopes | Any credential | 200 {object: "list", data[{scope, group, description, publishable, includes[], routes[{method, path}]}]} — every route each scope opens, and the read a write includes | Not in the OpenAPI spec |
GET /v1/accounts/me/balance | S, M, K, R, P · payments:read | 200 {totals[{currency, amount, available, pending}]} | EUR and USD only, from our ledger — not a bank balance. → Accounts |
GET /v1/platform/accounts | S, M, K, R · accounts:read (pk_ refused) | 200 array of {account_id, display_name, kyb_status, can_move_money, grant} | → Platforms |
GET /v1/utility/validate-address | Any credential | 200 {valid, reason?, ata_exists, asset?, estimated_rent} | Query address, chain (solana, ethereum, polygon, base, arbitrum), asset?. Syntax check; on Solana, also whether the token account exists — read from Solana devnet by default, in every environment, so do not rely on ata_exists for mainnet. |
Activity feed and contacts
GET /v1/transactions lists money coming in — bank transfers received
into your accounts, sandbox test funds, payments to your handle — and
transfers made in the dashboard. It is the only list of incoming payments. It
does not list payouts or conversions made through /v1/banking/*; those
are in GET /v1/banking/payouts.
GET /v1/transactions/{id} resolves an id from either list: the list's row
plus completed_at and, where they apply, reference and bank_details.
A payout or conversion id from GET /v1/banking/payouts reads direction
outgoing and type external — fx_conversion for an FX conversion, the
one an account closing makes included — whatever its kind: a payout to a
beneficiary, or, in the sandbox only, a send-back, a convert, a swap, or a
send on chain, to an IBAN, to a bank account outside SEPA or by @handle
(kind on
GET /v1/banking/payouts/{payout_id} says which). rail is its rail,
counterparty.name the payee's name as the payout recorded it — FX Treasury
on a banking FX conversion (type fx_conversion), and CashXChain on a
plan or fee payment from your balance, whose note says which — status
completed for settled, rolled_back for returned, failed for failed
or cancelled and pending otherwise, completed_at its settled_at, and
reference the reference it was sent with, when it has one. Such a banking
FX conversion carries no destination_amount or destination_currency
here: its bought leg is recorded on its quote, not on the payout.
bank_details — account_holder, bank_name and country where recorded,
account_number, and routing_number (the BIC where one was given, the sort
code or the routing number) — is served for a payout paid to a bank account,
as the payout recorded the account: an IBAN in full (a send-back to your own
EUR account included); a US or UK account or a CLABE paid from a
digital-asset account (sandbox only) as **** and its last four. It is absent for a payout
on chain, a swap, a send by @handle or to a Pix key, an Interac e-Transfer
and a cxc_handle payee.
| Route | Credentials · scope | Request | Success | Errors |
|---|---|---|---|---|
GET /v1/transactions | S, M, K, R, P · payments:read | limit (1–100, default 25), before, direction (incoming, outgoing, all) | 200 {transactions[], has_more, next_cursor?} | 403 |
GET /v1/transactions/{id} | as above | — | 200 transaction detail | 404 |
GET /v1/contacts | S, M, K, R, P · accounts:read | limit (1–100, default 50) | 200 {contacts[], has_more} | 403 |
POST /v1/contacts | S, M, K, R · accounts:write | {kind, name, email?, username?, country?, bank_name?, account_number?, routing_number?, iban?, swift?, currency?, network?, asset?, wallet_address?, note?} | 201 contact | 400, 404 username_not_found, 422 username codes |
PATCH /v1/contacts/{id} | as above | any of {name, email, username, country, bank_name, currency, note} | 200 contact | 403, 404 |
DELETE /v1/contacts/{id} | as above | — | 204 | 403, 404 |
/v1/transactions* is delegable with payments:read; contacts are not.
Digital assets (sandbox only)
Digital-asset accounts are on the money API: opened with
POST /v1/banking/accounts, listed by GET /v1/banking/accounts, and sent
from with POST /v1/banking/payouts. The routes of their own:
| Route | Credentials · scope | Body | 2xx | Errors |
|---|---|---|---|---|
GET /v1/banking/kyb/digital-assets?refresh= | S, M, K, R · accounts:read | — | 200 {state, can_open_account, blocked_reason, next_steps[], rails[], confirmed_at} | 503 digital_assets_unavailable |
POST /v1/banking/kyb/digital-assets Idem | S, M, K, R · accounts:write | {redirect_url, account_type?} | 201/200 {outcome, verification_url, terms_url, onboarding} | 503 digital_assets_unavailable |
GET /v1/banking/digital-assets/wallets | S, M, K, R · accounts:read | — | 200 {wallets[], networks[]} | 503 digital_assets_unavailable |
POST /v1/banking/digital-assets/wallets Idem | S, M, K, R · accounts:write | {network} | 201/200 wallet | 403 account_not_verified, 409 digital_assets_onboarding_required, 409 digital_assets_onboarding_incomplete, 422 network_unsupported |
GET /v1/banking/digital-assets/trusted-beneficiaries | S, M, K, R · accounts:read | — | 200 {trusted_beneficiaries[]} | 503 digital_assets_unavailable |
POST /v1/banking/digital-assets/trusted-beneficiaries Idem. Step-up. | S only — a person of the account | {kind, label, …} | 201 entry; 200 on a replay | 403 session_required, 409 trusted_beneficiary_exists, 422 travel_rule_incomplete, 422 corridor_unsupported, … |
DELETE /v1/banking/digital-assets/trusted-beneficiaries/{id} Step-up. | S only | — | 200 entry, revoked | 403 session_required, 404 |
POST /v1/sandbox/banking/kyb/digital-assets/approve | S, M, K, R · accounts:write | — | 200 onboarding status | 409 conflict, 429 rate_limited, 503 digital_assets_unavailable |
POST /v1/sandbox/banking/accounts/{account_id}/simulate-deposit Idem | S, M, K, R · payments:write | {amount, network?} | 202 {account_id, currency, network, amount, status} | 409 wallet_not_open, 422 account_not_digital, 422 asset_not_on_network, 429 rate_limited |
POST /v1/sandbox/banking/payouts/{payout_id}/simulate-arrival | S, M, K, R · payments:write | — | 200 {payout_id, payin_id, booked, linked} | 422 not_eligible, 429 rate_limited |
POST /v1/sandbox/banking/payouts/{payout_id}/simulate-completion | S, M, K, R · payments:write | — | 200 the payout (simulated: true once the sandbox settled it) | 409 not_simulatable, 422 not_eligible, 429 rate_limited, 503 service_unavailable |
→ Digital assets. Every code: Digital-asset errors.
The earlier /v1/casp/* routes were removed: they answer 404. Use the
routes above.
Customers
POST /v1/customers
CashXChain staff only — secret, restricted and master keys get 403, a
publishable key 401, and a request without Idempotency-Key gets 400
before either. Businesses join by signing up, not through this route.
Sandbox tools — sandbox only
The faucet takes a session or an API key with payments:write (S, M, K, R)
and is logged like a money route; its Idempotency-Key is optional — without
one every call credits again. adopt-corporate and /v1/sandbox/admin/whoami
take a dashboard session (S) only: every API key gets 401; they are not
rate-limited and not logged. The spec routes take no credential. None is
stopped by a lockdown.
| Route | Request | Success | Errors |
|---|---|---|---|
POST /v1/sandbox/banking/faucet | {currency, amount} — whole units, 1–100,000 | 200 {currency, amount, balance} | 400, 409, 409 idempotency_key_reuse, 422 asset_unsupported, 429 rate_limited |
POST /v1/sandbox/banking/adopt-corporate | {partner, external_id} | 200 {customer_id, partner, external_id, kyb_status, accounts_mirrored} | 400, 404, 409 |
GET /v1/sandbox/admin/whoami | — | 200 your account as the sandbox sees it | 403, 404 |
GET /v1/openapi.json, GET /v1/docs | — | The spec, Swagger UI | No credential |
The remaining /v1/sandbox/admin/* routes are internal sandbox tooling, not
part of the API.
Public routes
No credential.
| Route | Returns |
|---|---|
GET /health | {ok, service, version, environment, mail} |
GET /ready | {ok, db} — always HTTP 200; read ok |
GET /internal/version | {service, version, commit, commit_short, built_at?, environment} |
GET /v1/platform-status | {paused, message?, since?, confirmed, enforced} — see lockdown |
GET /v1/public/statements/{id} | A statement's summary and checksum, for third parties — Accounts |
GET /v1/public/pay/{handle} | A business's public pay page. 300 requests per hour per IP. |
POST /webhooks/banking/{partner_id}, POST /webhooks/casp/…, POST /webhooks/resend, POST /v1/webhooks/stripe/… | Inbound notifications from our partners, each verified by its own signature. Not for integrators. |
GET /v1/pay/{slug}, GET /v1/a/{slug}, POST /v1/a/{slug}/… | Payer-facing pages of Collect |
Dashboard routes
These serve the console and the web app. They take a dashboard session (S)
or a master key (M); every other key gets 401. They are listed so that
master-key holders know what exists; their request and response schemas are in
the OpenAPI spec, and their error codes in
Errors. None accepts Cxc-Account.
| Family | Routes |
|---|---|
| Account | POST /v1/accounts (attach a managed business — Platforms; Idem), GET /v1/accounts, GET /v1/accounts/{id}, GET /v1/accounts/me, GET /v1/accounts/me/vibans, GET /v1/accounts/me/closure-check, GET /v1/me, GET /v1/customers, GET /v1/customers/{id} |
| Grants | GET /v1/me/platform-permissions, DELETE /v1/platform-permissions/{id} |
| Event stream | POST /v1/notifications/ticket — then GET /v1/notifications with the cookie (Webhooks) |
| Billing | GET/POST /v1/billing/subscription, POST /v1/billing/subscription/preview, …/cancel, …/change-now, POST/DELETE /v1/billing/promotion-code, GET /v1/billing/invoices, GET /v1/billing/invoices/{id}/pdf, GET/POST/PUT /v1/billing/payment-method, GET /v1/billing/catalogue, GET /v1/billing/entitlements |
| Usernames and pay page | GET/PUT /v1/accounts/me/username, GET /v1/usernames/check, …/search, …/suggest, GET /v1/usernames/{username}, GET/PUT /v1/pay-page, POST /v1/pay-page/links, DELETE /v1/pay-page/links/{id}, GET /v1/crypto-addresses/me |
| Collect, people-only | POST /v1/collect/accounts (Idem; seat collect.manage), POST /v1/collect/account-session (collect.manage), GET/POST /v1/collect/groups, POST /v1/collect/groups/preview, …/csv, POST /v1/collect/anlaesse, GET /v1/collect/anlaesse/{anlass_id}, …/export |
Collect's merchant routes are on the developer surface instead, so a secret
key or a restricted key with the scope reaches them: GET /v1/collect/status,
GET /v1/collect/payment-links, GET …/{slug}/payments, GET /v1/collect/payments
and GET /v1/collect/refunds with collect:read; POST /v1/collect/payment-links
(Idem), PATCH …/{slug} and POST /v1/collect/payments with collect:write;
POST /v1/collect/payments/{payment_intent_id}/refunds (Idem) with
refunds:write. A dashboard session needs the seat permission
collect.links.create for those writes. The /v1/claims/* routes and the
claims:* scopes were removed in October 2026.
Not for integrators
/internal/* — CashXChain staff and service-to-service routes. No API key of
any type reaches them, master keys included. GET /internal/version (above) is
the only public one.
OpenAPI coverage
The spec is served in the sandbox only, at
https://api.sandbox.cashxchain.com/v1/openapi.json (Swagger UI at /v1/docs).
It describes production too. Mounted routes it does not describe:
GET /v1/meta/scopesPOST /v1/banking/kyb/referrals/{external_id}/parent-entitiesand its…/documents/slotsGET /v1/billing/invoices,GET /v1/billing/invoices/{id}/pdf,POST /v1/billing/subscription/change-now/v1/collect/groups*and/v1/collect/anlaesse*GET /health,GET /ready,GET /internal/version,/v1/a/*, and the inbound webhook receivers
Where the spec and this page disagree, this page was written from the code.
Each operation's security requirement in the spec comes from the same
route-policy table that GET /v1/meta/scopes publishes, so which credential
and scope a route takes is accurate there. Known differences:
- Its description says only
/healthand/readyneed no credential — the public routes need none either. - It gives
422for a beneficiary that fails corridor validation — the API answers400 bad_request. - It lists
/internal/admin/*routes, which no integrator credential can call.