Skip to main content

Payments & Transfers

CashXChain has two payment surfaces:

  • Transfers (/v1/transfers) — the primary, high-level API for sending money. Use this for most integrations.
  • Payments (/v1/payments) — the low-level partner passthrough API for add-funds and claimless sends via a specific partner.

Money movement is created with a write call and tracked as transactions (/v1/transactions).


Transfers

POST /v1/transfers

Creates a transfer. The API auto-detects the transfer type from the body shape:

  • Send recipient_email for an internal transfer (CashXChain user to CashXChain user).
  • Send recipient (object with bank) for an external transfer (to a bank account).

Auth: Any valid credential. Scope required for rk_ keys: transfers:write.
Header: Idempotency-Key: <unique-string> — required.

Internal transfer (email-to-email)

POST /v1/transfers
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: invoice-2026-001
{
"recipient_email": "[email protected]",
"amount": "50.00",
"currency": "USD",
"note": "INV-2026-001"
}

Supported currencies in sandbox:

  • USD, EUR, GBP → routed via MockPartner (instant, deterministic)
  • USDC, EURC → routed via Coinbase CDP on Solana

External transfer (bank account)

{
"recipient": {
"email": "[email protected]",
"name": "Vendor GmbH",
"bank": {
"country": "DE",
"bank_name": "Deutsche Bank",
"account_number": "DE89370400440532013000",
"routing_number": "37040044",
"currency": "EUR"
}
},
"amount": "1000.00",
"currency": "EUR",
"note": "Invoice payment"
}

Sandbox note: External transfers return 400 external_send_blocked unless the bank details match a registered sandbox user.

Response (201 Created)

{
"transfer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "executing",
"amount": "50.00",
"currency": "USD",
"estimated_arrival": "within minutes",
"veem_payment_id": "mock-pay-abc123"
}

Transfer status lifecycle

pending → quoting → executing → completed
↘ failed
↘ rolled_back
StatusMeaning
pendingReceived and queued.
quotingFetching FX rate or routing information.
executingSubmitted to the downstream partner rail.
completedSettled successfully.
failedTerminal failure. The transaction will not proceed.
rolled_backExecution started but was reversed.

GET /v1/transfers

List transfers for the caller's account.

Auth: Any valid credential. Scope required for rk_ keys: transfers:read.

GET /v1/transfers
Authorization: Bearer <token>

GET /v1/transfers/{id}

Retrieve a single transfer by UUID.

Auth: Any valid credential. Scope required for rk_ keys: transfers:read.

GET /v1/transfers/3fa85f64-5717-4562-b3fc-2c963f66afa6
Authorization: Bearer <token>

Transactions

Transactions are the ledger records produced by transfers. Read transactions to track the status of money movement.

GET /v1/transactions

GET /v1/transactions?limit=50
Authorization: Bearer <token>

Query parameters:

ParamTypeDescription
limitintegerMax rows (default 50, max 200).
beforeuuidCursor for pagination — returns rows before this transaction ID.

Auth: Scope required for rk_ keys: transactions:read.

GET /v1/transactions/{id}

GET /v1/transactions/3fa85f64-5717-4562-b3fc-2c963f66afa6
Authorization: Bearer <token>

Payments (low-level partner API)

The payments API provides direct access to partner-level funding operations. Use it when you need fine-grained control over which partner handles a transaction or when building treasury/wallet top-up flows.

POST /v1/payments/add-funds

Add funds to a customer's wallet via a specific partner.

Auth: Any valid credential. Scope required for rk_ keys: payments:write.
Header: Idempotency-Key: <unique-string> — required.

POST /v1/payments/add-funds
Authorization: Bearer <token>
Content-Type: application/json
Idempotency-Key: add-funds-2026-001
{
"customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"partner": "veem",
"amount": "500.00",
"currency": "USD"
}

Optional: include source_funding_method_external_id to specify the partner-side funding method. If omitted, it is auto-discovered.

Response (201 Created)

{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"partner": "veem",
"direction": "outbound",
"kind": "add_funds",
"amount": "500.00",
"currency": "USD",
"status": "authorized",
"external_id": "mock-pay-xyz",
"claim_link": null,
"idempotency_key": "add-funds-2026-001",
"retry_count": 0,
"created_at": "2026-06-08T00:00:00Z",
"completed_at": null
}

POST /v1/payments/send

Send a claimless payment to an external payee via a specific partner.

Auth: Any valid credential. Scope required for rk_ keys: payments:write.
Header: Idempotency-Key: <unique-string> — required.

{
"customer_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"partner": "veem",
"amount": "250.00",
"currency": "USD",
"purpose": "Goods and Services",
"source_funding_method_external_id": "<wallet-external-id>",
"source_funding_method_kind": "wallet",
"payee": {
"email": "[email protected]",
"first_name": "John",
"last_name": "Smith",
"country_code": "US",
"phone_country_code": "+1",
"phone_number": "5551234567",
"kind": "business",
"business": {
"business_name": "Smith Corp",
"industry": "Technology",
"sub_industry": "Software",
"entity": "LLC",
"tax_id_number": "12-3456789",
"address": {
"country_code": "US",
"street": "123 Main St",
"city": "New York",
"province": "NY",
"postal_code": "10001"
}
},
"bank": {
"iso_country_code": "US",
"bank_name": "JPMorgan Chase",
"currency_code": "USD",
"bank_account_number": "123456789",
"routing_number": "021000021"
}
}
}

source_funding_method_kind values: wallet | bank | viban

payee.kind values: personal | business


GET /v1/payments

List payments for the caller's account.

Auth: Any valid credential. Scope required for rk_ keys: payments:read.

GET /v1/payments?limit=50&status=pending&customer_id=<uuid>
Authorization: Bearer <token>

Query parameters:

ParamTypeDescription
customer_iduuidFilter by customer. Non-admin callers are pinned to their own account.
statusstringFilter by payment status.
limitintegerMax rows (default 50, max 200).

GET /v1/payments/{id}

Retrieve a single payment by UUID.

Auth: Any valid credential. Scope required for rk_ keys: payments:read.


Payment object fields

FieldTypeDescription
iduuidPayment UUID.
partnerstringPartner that executed the payment (veem, coinbase, circle, stablegate).
directionstringoutbound or inbound.
kindstringadd_funds, claimless, or send.
amountdecimal stringPayment amount.
currencystringISO 4217 currency code.
statusstringSee lifecycle below.
external_idstring | nullPartner-assigned payment ID.
claim_linkstring | nullClaim URL for claimless payments, if applicable.
idempotency_keystringThe Idempotency-Key submitted with the request.
retry_countintegerNumber of times this payment has been retried.
created_atISO 8601Creation timestamp.
completed_atISO 8601 | nullSettlement timestamp.

Payment status lifecycle

pending → sent → authorized → completed
↘ failed
↘ cancelled
StatusMeaning
pendingPersisted, not yet submitted to partner.
sentSubmitted to partner — awaiting confirmation.
authorizedPartner has authorized the transaction.
completedFully settled.
failedTerminal failure.
cancelledCancelled before execution.

Idempotency

All write endpoints (POST /v1/transfers, POST /v1/payments/add-funds, POST /v1/payments/send) require an Idempotency-Key header.

Idempotency-Key: invoice-2026-001
  • Use a unique string per distinct operation — an invoice number, UUID, or timestamp.
  • Repeating the same key returns the cached response (200 OK) without re-executing.
  • Prevents double-charges on network retries, timeouts, or 5xx errors.
  • The idempotency_key is included in the response for correlation.