Payments API Reference
All payment endpoints are mounted at /v1/payments. Authentication via X-API-Key or Authorization: Bearer is required unless noted.
Endpoints summary
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /v1/payments | Required (Basic KYC) | Create a payment request |
GET | /v1/payments | Required | List payments (paginated) |
GET | /v1/payments/{payment_id} | Required | Get payment detail |
GET | /v1/pay/{payment_id}/status | None | Public status poll (payer-facing) |
GET | /v1/payments/tokens | Required | List tokens available for payments |
GET | /v1/pay/{payment_id} | None | Public payment page data |
POST | /v1/pay/{payment_id}/confirm | None | Payer confirms crypto selection and locks rate |
GET | /v1/pay/{payment_id}/fee-estimates | None | Live network fee estimates per token |
POST /v1/payments
Create a new payment request. Requires Basic KYC.
Request body:
| Field | Type | Required | Constraints |
|---|---|---|---|
amount_usd | decimal | Yes | Minimum 1.00 |
description | string | No | Max 500 characters |
redirect_url | string | No | Must start with https:// |
allowed_token_ids | array of UUIDs | No | Must be UUIDs of active tokens in user's enabled set |
Response (201 Created):
{
"success": true,
"data": {
"payment_id": "9f1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
"status": "AWAITING_SELECTION",
"amount_usd": "150.00",
"description": "Order #1001",
"pay_url": "https://app.tokencashflow.com/pay/9f1b2c3d-...",
"selection_expires_at": null,
"created_at": "2026-05-16T12:00:00Z",
"fee_payer": "user"
}
}Error codes:
| Code | Condition | |---|---|---| | KYC_REQUIRED | User's KYC level is below Basic | | VALIDATION_ERROR | Invalid field values | | RATE_STALE | No fresh exchange rate available for any enabled token |
GET /v1/payments
List the authenticated user's payments.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by payment status (e.g. completed) |
date_from | ISO 8601 date | Payments created on or after |
date_to | ISO 8601 date | Payments created on or before |
page | integer | Default: 1 |
per_page | integer | Default: 20, max: 100 |
Response (200 OK):
{
"success": true,
"data": {
"payments": [
{
"payment_id": "...",
"status": "COMPLETED",
"amount_usd": "150.00",
"net_settlement_usd": "147.75",
"created_at": "2026-05-16T12:00:00Z",
"completed_at": "2026-05-16T12:05:00Z"
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 87,
"total_pages": 5
}
}
}GET /v1/payments/
Get full detail for a single payment.
Response (200 OK):
{
"success": true,
"data": {
"payment_id": "9f1b2c3d-...",
"status": "COMPLETED",
"amount_usd": "150.00",
"description": "Order #1001",
"redirect_url": "https://yourstore.com/thank-you",
"fee_payer": "user",
"token": { "id": "...", "symbol": "ETH", "name": "Ethereum", "chain": "ethereum" },
"locked_rate_usd": "2000.00",
"locked_at": "2026-05-16T12:01:00Z",
"required_crypto_amount": "0.075000000000000000",
"total_received_crypto": "0.075000000000000000",
"total_received_usd": "150.00",
"service_fee_usd": "0.75",
"conversion_fee_usd": "1.50",
"net_settlement_usd": "147.75",
"selection_expires_at": null,
"created_at": "2026-05-16T12:00:00Z",
"completed_at": "2026-05-16T12:05:00Z",
"transactions": [
{
"tx_hash": "0xabc123...",
"from_address": "0xpayer...",
"amount_crypto": "0.075",
"block_height": 19000000,
"confirmations": 15,
"is_confirmed": true,
"detected_at": "2026-05-16T12:02:00Z",
"confirmed_at": "2026-05-16T12:04:00Z"
}
]
}
}Error codes:
| Code | Condition |
|---|---|
NOT_FOUND | payment_id does not exist or does not belong to the authenticated user |
GET /v1/pay/{payment_id}/status
No authentication required. Used by the payment page for polling.
Returns only safe public fields.
Response (200 OK):
{
"success": true,
"data": {
"payment_id": "9f1b2c3d-...",
"status": "AWAITING_PAYMENT",
"amount_usd": "150.00",
"required_crypto_amount": "0.075000000000000000",
"total_received_crypto": "0.000000000000000000",
"selection_expires_at": "2026-05-16T12:31:00Z"
}
}
selection_expires_atisnulluntil the payer confirms a token selection. Once set, it controls the countdown timer on the payment page and is reset on each reselection. Payments have no global expiry.
GET /v1/payments/tokens
List active tokens available to the authenticated user for payment creation.
Response (200 OK):
{
"success": true,
"data": {
"tokens": [
{
"id": "a1b2c3d4-...",
"symbol": "ETH",
"name": "Ethereum",
"logo_url": "https://...",
"decimals": 18,
"is_stablecoin": false,
"chain": {
"id": "...",
"name": "Ethereum",
"short_name": "eth",
"chain_type": "evm"
}
}
]
}
}POST /v1/pay/{payment_id}/confirm
No authentication required. Payer confirms crypto/chain selection. Locks the rate and starts the selection_expires_at countdown.
Can also be called from EXPIRED_SELECTION status to reselect a token (new rate, new window).
Request body:
| Field | Type | Required | Description |
|---|---|---|---|
token_id | UUID | Yes | ID of the selected token |
Response (200 OK):
{
"success": true,
"data": {
"payment_id": "9f1b2c3d-...",
"status": "AWAITING_PAYMENT",
"crypto_symbol": "ETH",
"chain": "ethereum",
"wallet_address": "0xpaymentwallet...",
"locked_rate_usd": "2000.00",
"required_crypto_amount": "0.075000000000000000",
"selection_expires_at": "2026-05-16T12:31:00Z"
}
}Error codes:
| Code | Condition |
|---|---|
NOT_FOUND | Payment does not exist |
INVALID_STATE | Payment is not in AWAITING_SELECTION or EXPIRED_SELECTION |
UNCONFIRMED_TX | A transaction is currently being confirmed — reselection blocked |
RATE_STALE | No fresh rate available; try again |
TOKEN_NOT_ALLOWED | Token not in the allowed set for this payment |
Payment status reference
| Status | Description |
|---|---|
AWAITING_SELECTION | Created; payer has not yet selected a token |
AWAITING_PAYMENT | Token selected; rate locked; waiting for on-chain transaction |
PARTIALLY_PAID | On-chain payment received but less than required; waiting for more |
EXPIRED_SELECTION | Chain selection window expired; payer must reselect to continue |
CONFIRMING | Full amount received; waiting for block confirmations |
CONFIRMED | Block confirmations complete; sweep in progress |
COMPLETED | Payment fully settled; funds credited to merchant |
EXPIRED | Payment expired in AWAITING_SELECTION — no selection was ever made |
BLACKLISTED | Transaction detected from a blacklisted address |
SWEEP_FAILED | Sweep to main wallet failed after maximum retries |
FAILED | System error requiring manual investigation |

