Skip to main content

Platforms

A platform is a business that runs CashXChain for other businesses — a marketplace paying out its sellers, an ERP paying its customers' suppliers. Each of those businesses keeps its own verified account, its own bank accounts and its own money; the platform sends instructions on their behalf and never holds their funds.

The pieces:

TermMeaning
Master key (cxc_mk_…)The platform's credential. Issued by CashXChain.
Managed accountA business you have attached. It signed up and is verified in its own right.
GrantThe record that lets you act for one managed account, with a set of scopes. Either side can revoke it.
Cxc-Account headerNames the managed account a request acts for.

Master keys​

The developer console calls them Platform keys; they are the same thing. Master keys are not self-service: a master key reaches accounts other than your own, so we issue it deliberately. Ask from the console's API keys page or at [email protected], and say what you are building and whether you need to move money for the businesses you serve.

  • A master key can do everything a secret key can, plus what a secret key cannot: key management (/v1/api-keys) and the dashboard surface (/v1/accounts, billing) — see what each credential can reach.
  • It skips scope checks for itself. When it acts for someone else, the grant's scopes are checked instead — see below.
  • Moving a managed account's money is a separate permission, off when the key is issued and enabled per key by CashXChain. Without it, a master key can read for and onboard the businesses it serves, but not open accounts, register beneficiaries, pay out or convert for them.
  • A master key never steps up and never reaches /internal/*.
  • It cannot be created through POST /v1/api-keys. Only CashXChain staff can issue or revoke one, and revocation takes effect on the next request. If one is exposed, tell us immediately.

Attach a business​

The business signs up for CashXChain itself first. Then you attach to it by its email:

HTTP
POST /v1/accounts
Authorization: Bearer cxc_mk_…
Idempotency-Key: attach-acme-gmbh
Content-Type: application/json

{"contact_email": "[email protected]", "display_name": "Acme GmbH", "country_code": "DE"}

201 returns the account and a platform_permission_id — the grant. The grant starts with accounts:read, accounts:write, payments:read and fx:read; payments:write is added by CashXChain.

ErrorWhen
409 conflictNo CashXChain account has that email — the business signs up first; or it has never signed in; or it already belongs to another platform
200It is already yours — the same account is returned

A platform attaches to an identity whose owner is already in control of it; it cannot reserve one. The business can see your grant at GET /v1/me/platform-permissions and end it.

The business then completes verification — you can drive it for them with Cxc-Account. They are approved by our banking partner in their own right.

See who you can act for​

HTTP
GET /v1/platform/accounts
JSON
[
{
"account_id": "6f1c2b9e-0d1a-4c3e-9f7a-2b8d4e6a1c00",
"display_name": "Acme GmbH",
"kyb_status": "active",
"can_move_money": true,
"grant": {"id": "…", "scopes": ["accounts:read", "accounts:write", "payments:read", "fx:read"], "kyc_mode": "direct", "granted_at": "…"}
}
]

grant.scopes are exactly the scopes checked when you act for that account. In the sandbox can_move_money can read false for a pending account that the sandbox would in fact let pay out.

End the relationship​

DELETE /v1/platform-permissions/{id} — callable by the platform or the managed business. The record is kept; your authority ends at once. The business keeps its account, its bank accounts and its money.


Act for a managed account​

Add one header:

HTTP
POST /v1/banking/payouts
Authorization: Bearer cxc_mk_…
Cxc-Account: 6f1c2b9e-0d1a-4c3e-9f7a-2b8d4e6a1c00
Idempotency-Key: acme-payout-INV-0042

The request then acts entirely as the managed account: the payout, the debit, the limits and the verification check are theirs. Without the header, your key acts as you. Naming your own account is the same as not sending the header.

Who may send it. Your account must be a platform, you must hold a live grant for the named account, and the grant must carry the route's scope. Beyond that, it depends on the route:

Route kindAllowed credentials
Reads, quotes, KYB, primary-currency changeSession, mk_, sk_, or an rk_ key carrying the route's scope. Never pk_.
Money: open an account, register a beneficiary, payout, FX executeSession, or mk_ with the money permission

What you can call. Only these routes accept Cxc-Account; every other route refuses it with 403 delegation_not_supported:

Scope in the grantRoutes
accounts:readGET /v1/banking/accounts, …/{account_id}/payin-instructions, …/{account_id}/balance, …/plan-impact, GET /v1/banking/beneficiaries, GET /v1/banking/corridors, GET /v1/banking/settlement/*, GET /v1/banking/kyb/precheck-records, GET /v1/banking/kyb/referrals/{external_id}
accounts:writeEvery POST /v1/banking/kyb/* route, PUT /v1/banking/accounts/primary, money: POST /v1/banking/accounts
beneficiaries:writemoney: POST /v1/banking/beneficiaries. Every grant that held accounts:write before October 2026 was given this scope too, so no platform lost payee registration.
payments:readGET /v1/banking/payouts, GET /v1/banking/payouts/{payout_id}, POST /v1/banking/payouts/quote, GET /v1/banking/payout-options, GET /v1/transactions*
payments:writemoney: POST /v1/banking/payouts, POST /v1/banking/fx/execute
fx:readGET /v1/fx/rates, POST /v1/banking/fx/quote

Digital assets (sandbox). Digital-asset onboarding (GET/POST /v1/banking/kyb/digital-assets) is delegable under accounts:read / accounts:write as above, and GET /v1/banking/capabilities with a grant carrying accounts:read. Acting for a managed account, a platform cannot send from its digital-asset account to a third party or back to its EUR account (403 session_required: the payment needs the customer's own person or key), nor approve its trusted beneficiaries (403 delegation_not_supported). actions[].allowed_by_policy in Capabilities says, route by route, what a delegated caller is admitted to. The earlier /v1/casp/* routes were removed.

Not delegable, among others: beneficiary verification and accept-verification, account close, statements, settlement policy changes, API keys, request logs, usage, billing, webhooks.

Refusals​

Every refusal except the last row carries details, telling you what to fix.

StatusCodeCausedetails
400delegation_malformedThe header is not a UUIDdiscover
400delegation_conflict?account_id= sent as wellheader, query_param
403delegation_not_supportedThis route cannot be delegatedmethod, path
403delegation_not_permittedA publishable key or public session token, or your account is not a platformcredential or api_tier, required_api_tier
403delegation_no_grantNo live grant for that account — or no such accountaccount_id, discover
403delegation_scope_deniedThe grant lacks the route's scoperequired_scope, granted_scopes, grant_id
403delegation_requires_sessionA money route with a secret or restricted key, or a master key without the money permissioncredential, capability, grant_id
403account_not_verifiedA money route for a managed account that is not active (production), refused before anything is sent — the same answer your own account gets, with delegated: true. The sandbox lets a pending managed account through and refuses a suspended one the same way.account_id, kyb_status, next_action, delegated, referral
503service_unavailableThe decision could not be recorded; nothing was done—

discover points at GET /v1/platform/accounts. Every decision — allowed or refused — is recorded before the request runs, except a malformed or conflicting header, which is refused before we know whom it concerns.

Things to know​

  • Request logs record a delegated call under your account, the one whose credential made it, not the managed account's.
  • Idempotency keys belong to the account the request acts for. Two managed accounts can use the same key without colliding.
  • Step-up, for a dashboard session, is bound to the person signed in, not to the managed account.
  • The header is checked on the developer, money and dashboard surfaces, where only the routes in the table accept it. On the public and sandbox routes it is ignored, and the request acts as you.