Skip to main content
Banking gives you real bank accounts in the name of your verified profile: you receive funds over international rails (SEPA, SWIFT, ACH depending on the currency), hold fiat balances and send payments to third parties. It is a separate product from your USDT balance: banking money lives in your bank accounts, not in the CBPay balance.
Banking fees come in two shapes. Standalone fixed fees (banking_customer, banking_account, banking_operation) are debited from your USDT balance when each operation executes and refunded automatically if it fails. Transactional rail fees (banking_deposit, banking_transfer_ach, banking_transfer_swift, banking_transfer_wire, banking_transfer_sepa) are a percentage plus a fixed amount charged in the operation currency (your BANK_USD / BANK_EUR balance) — see rail fees. With a fee of 0 (the default) the service is free. The banking_fee and banking_fee_asset fields on each response show what was charged and in which currency.

The full flow

  1. Create your banking profile (POST /v1/banking/customer) — once.
  2. Upload verification documents and submit for review.
  3. Once approved, open accounts per currency.
  4. Register beneficiaries (counterparties) for third-party payments.
  5. Send payments: quote with prepare, execute with operations.
State changes arrive through the banking_customer_status_changed and banking_operation_status_changed webhooks (webhooks).

1. Create your banking profile

Once per account. If you omit type, name or email, they are filled from your CBPay account:
Response 201:
If your account already has a banking profile — 409 banking_customer_exists.
Application review. If your organization enabled banking application review, this request can answer 202 Accepted with {"status":"in_review","kind":"banking_application","review_id":"…"} instead of 201 — the profile is created only when compliance approves the review. The banking profile fee is charged when the application is held and refunded automatically if it is rejected. Track the result with the webhook txn_review_status_changed or in Transaction reviews.
Check the state at any time:

2. Documents and verification

Upload each document as base64 (free):
Then submit the profile for review (free):
Profile states: draftsubmittedunder_reviewapproved or rejected. The banking_customer_status_changed webhook notifies each change — for your own profile (customer_kind: self) and for the third parties you register (customer_kind: third_party, with their third_party_id):

3. Open bank accounts

With the profile approved, create one account per currency. Available currencies: USD (ACH/Fedwire/SWIFT rails) and EUR (SEPA/SWIFT):
Response 201data carries the details to receive funds (account number/IBAN, routing, bank):
List your accounts, fetch the detail of a specific account, and check balances:
The detail endpoint (GET /v1/banking/accounts/{id}) returns the account LIVE — name, currency, status, and under data the requirements to receive funds (wire and local rails: bank, account number/IBAN, routing). Use it to show the deposit instructions of a specific account without walking the list:
source tells you where the detail came from: live (the bank answered in real time) or mirror (the bank could not serve the account at that moment and the last known snapshot is returned — deposit requirements remain available).
The list exposes only the accounts enabled for your operation according to the corridor configuration. A non-enabled account does not appear in the list and its by-id queries respond 404 not_found.
Limit for person accounts: a person account can hold at most 1 bank account. Attempting a second one returns 409 banking_account_limit. Company accounts have no limit.

Third-party users (companies only)

If your account is a company, besides your own accounts you can register third-party banking users — your end clients (persons or companies) — each with their own identity and verification and bank accounts in their name. No limit on third parties or accounts per third party.

Registering the third party

Registration requires the verification_id of an approved KYC/KYB verification of the third party — their single identity inside CBPay. The type comes from the verification kind (KYC ⇒ INDIVIDUAL, KYB ⇒ COMPANY), the data (name, email, address) auto-fills from the verified profile (whatever you send explicitly wins), and the already-validated documents are re-delivered automatically to the banking provider. The banking profile fee is charged (refunded if the registration fails):
Response 201:
documents_synced counts the verification documents that were loaded automatically into the third party’s banking profile. If one could not be synced (or the bank requests additional categories), upload it through the manual document flow below and then submit.
Application review. With banking application review enabled, registering a third party can also answer 202 Accepted (kind: banking_application): the third party is registered only when the review is approved, and the registration fee is refunded automatically on rejection. Track it via txn_review_status_changed or Transaction reviews.
Save the third_party_id: every third-party route uses it. List and fetch (the GET carries the live verification status):

Third-party verification (free)

Same as your own profile, but on the third party:

Third-party accounts

Once the third party is approved, open accounts for them (same banking_account fee) and operate just like your own:
  • Each third party belongs to you and only you: another CBPay account can never see or operate it (it gets 404).
  • A person account attempting to create third parties receives 403 company_required.
  • Without verification_id (or with a non-approved verification) the registration answers 422 verification_required / 422 verification_not_approved. If you send a type that does not match the verification kind, 422 verification_kind_mismatch. Third parties created before this rule keep operating normally.
  • Registered third parties feed the “new users” metric of your account summary.

4. Register beneficiaries

To pay third parties, first register the beneficiary with their banking details (free; it goes through moderation before it can be used):
List yours with GET /v1/banking/counterparties and attach more accounts to an existing beneficiary with POST /v1/banking/counterparties/{id}/accounts.

5. Send payments

Two operation types: Quote first (free, moves no money):
Execute with an idempotency key (the rail fee — or the legacy banking_operation fee when the rail has no configuration — is charged here):
Response 202:
banking_fee and banking_fee_asset only appear when a fee was charged. With a per-rail fee the asset is the operation currency (BANK_USD / BANK_EUR); with the legacy fallback it is USDT.
  • The final state arrives through the banking_operation_status_changed webhook (completed / failed); you can also poll GET /v1/banking/operations/{id}. Once the operation reaches a final state, the webhook includes its receipt_url and you can download the PDF receipt with GET /v1/banking/operations/{id}/receipt (receipts).
  • Retries with the same Idempotency-Key return the original operation (idempotency_hit: true) without charging the fee again.
Complete traceability. Every banking operation is recorded on your account: it shows up in the banking_operations section of the statement, its money reconciles in the BANK_USD/BANK_EUR mirror balances (assets section), and its volume adds to the gross_volume in analytics. The authoritative balance remains the bank’s: the mirror is reconciled periodically.
The full history, with filters:
Every banking operation — including inbound deposits and bank fees discovered automatically from the bank — exposes its direction (in / out), net amount, currency, counterparty and reference whenever the bank reports them. These fields are optional and appear in GET /v1/banking/operations and GET /v1/banking/operations/{id}.
The banking_operation_status_changed webhook stays lightweight by design: it carries the identifiers and the new status only, never the enriched fields. When it fires, fetch the operation detail to read the direction, amount, counterparty and reference. See webhooks.

Rail fees (deposits and transfers)

On top of the standalone fixed fees, banking supports transactional fees per rail — a percentage plus a fixed amount, always charged in the operation currency (BANK_USD / BANK_EUR), never in USDT: For transfers the available balance must cover amount + fee — if it does not, the API answers 402 insufficient_funds and the operation is not created. If the operation is definitively rejected right after dispatch, the fee is refunded automatically (same discipline as the legacy fee). Fallback: if the rail has no specific configuration (neither at account nor at platform level), the legacy banking_operation fee (fixed, in USDT) applies. A rail configured with 0% + 0 fixed is explicitly free — it does not fall back to the legacy fee.

Operation statuses

Errors

The general error catalog lives in Errors.

FAQ

No. Banking money lives in your bank accounts and is queried with GET /v1/banking/accounts/{id}/balance. The authoritative balance is the bank’s; your statement reconciles it in the BANK_USD/BANK_EUR mirror balances. Per-rail fees are charged in the operation currency (your BANK_USD/BANK_EUR balance); only the legacy banking_operation fallback fee is debited from your USDT balance.
It is refunded automatically — profile, account and operation fees alike, including per-rail fees (refunded on the definitive synchronous rejection). A retry with the same Idempotency-Key returns the original operation (idempotency_hit: true) and never charges twice.
One per currency (USD, EUR). Additionally, person accounts can hold at most 1 bank account in total (409 banking_account_limit); company accounts have no limit.
The list only exposes accounts enabled for your operation per the corridor configuration. A non-enabled account does not appear and its by-id queries answer 404 not_found — contact your CBPay team if you need it enabled.
No — third parties are a company feature (403 company_required). Registration also requires the verification_id of an approved KYC/KYB verification of the third party.
Subscribe to banking_operation_status_changed: it fires on completed / failed and includes the receipt_url once final. You can also poll GET /v1/banking/operations/{id}.
Last modified on August 10, 2026