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_emailfor an internal transfer (CashXChain user to CashXChain user). - Send
recipient(object withbank) 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_blockedunless 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
| Status | Meaning |
|---|---|
pending | Received and queued. |
quoting | Fetching FX rate or routing information. |
executing | Submitted to the downstream partner rail. |
completed | Settled successfully. |
failed | Terminal failure. The transaction will not proceed. |
rolled_back | Execution 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:
| Param | Type | Description |
|---|---|---|
limit | integer | Max rows (default 50, max 200). |
before | uuid | Cursor 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:
| Param | Type | Description |
|---|---|---|
customer_id | uuid | Filter by customer. Non-admin callers are pinned to their own account. |
status | string | Filter by payment status. |
limit | integer | Max 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
| Field | Type | Description |
|---|---|---|
id | uuid | Payment UUID. |
partner | string | Partner that executed the payment (veem, coinbase, circle, stablegate). |
direction | string | outbound or inbound. |
kind | string | add_funds, claimless, or send. |
amount | decimal string | Payment amount. |
currency | string | ISO 4217 currency code. |
status | string | See lifecycle below. |
external_id | string | null | Partner-assigned payment ID. |
claim_link | string | null | Claim URL for claimless payments, if applicable. |
idempotency_key | string | The Idempotency-Key submitted with the request. |
retry_count | integer | Number of times this payment has been retried. |
created_at | ISO 8601 | Creation timestamp. |
completed_at | ISO 8601 | null | Settlement timestamp. |
Payment status lifecycle
pending → sent → authorized → completed
↘ failed
↘ cancelled
| Status | Meaning |
|---|---|
pending | Persisted, not yet submitted to partner. |
sent | Submitted to partner — awaiting confirmation. |
authorized | Partner has authorized the transaction. |
completed | Fully settled. |
failed | Terminal failure. |
cancelled | Cancelled 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_keyis included in the response for correlation.