Skip to content

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

MethodPathAuthDescription
POST/v1/paymentsRequired (Basic KYC)Create a payment request
GET/v1/paymentsRequiredList payments (paginated)
GET/v1/payments/{payment_id}RequiredGet payment detail
GET/v1/pay/{payment_id}/statusNonePublic status poll (payer-facing)
GET/v1/payments/tokensRequiredList tokens available for payments
GET/v1/pay/{payment_id}NonePublic payment page data
POST/v1/pay/{payment_id}/confirmNonePayer confirms crypto selection and locks rate
GET/v1/pay/{payment_id}/fee-estimatesNoneLive network fee estimates per token

POST /v1/payments

Create a new payment request. Requires Basic KYC.

Request body:

FieldTypeRequiredConstraints
amount_usddecimalYesMinimum 1.00
descriptionstringNoMax 500 characters
redirect_urlstringNoMust start with https://
allowed_token_idsarray of UUIDsNoMust be UUIDs of active tokens in user's enabled set

Response (201 Created):

json
{
  "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:

ParameterTypeDescription
statusstringFilter by payment status (e.g. completed)
date_fromISO 8601 datePayments created on or after
date_toISO 8601 datePayments created on or before
pageintegerDefault: 1
per_pageintegerDefault: 20, max: 100

Response (200 OK):

json
{
  "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):

json
{
  "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:

CodeCondition
NOT_FOUNDpayment_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):

json
{
  "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_at is null until 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):

json
{
  "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:

FieldTypeRequiredDescription
token_idUUIDYesID of the selected token

Response (200 OK):

json
{
  "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:

CodeCondition
NOT_FOUNDPayment does not exist
INVALID_STATEPayment is not in AWAITING_SELECTION or EXPIRED_SELECTION
UNCONFIRMED_TXA transaction is currently being confirmed — reselection blocked
RATE_STALENo fresh rate available; try again
TOKEN_NOT_ALLOWEDToken not in the allowed set for this payment

Payment status reference

StatusDescription
AWAITING_SELECTIONCreated; payer has not yet selected a token
AWAITING_PAYMENTToken selected; rate locked; waiting for on-chain transaction
PARTIALLY_PAIDOn-chain payment received but less than required; waiting for more
EXPIRED_SELECTIONChain selection window expired; payer must reselect to continue
CONFIRMINGFull amount received; waiting for block confirmations
CONFIRMEDBlock confirmations complete; sweep in progress
COMPLETEDPayment fully settled; funds credited to merchant
EXPIREDPayment expired in AWAITING_SELECTION — no selection was ever made
BLACKLISTEDTransaction detected from a blacklisted address
SWEEP_FAILEDSweep to main wallet failed after maximum retries
FAILEDSystem error requiring manual investigation

TokenCashFlow Documentation