Getting Started
This section covers the Checker Payments API.
Payments API Guides
Use the Payments API to onboard customers, open accounts, register beneficiaries, and move money across supported corridors. Every corridor follows the same sequence:
Create a customer → Onboard → Open an account → Fund it → Add a beneficiary → Send a payout
Two corridors are documented here:
| Use case | Accounts | Funding | Payouts |
|---|---|---|---|
| USD Accounts | USD account plus stablecoin wallets, in the customer's own name | Inbound bank credit (ACH, Fedwire, SWIFT) | Bank payout on ACH, FEDWIRE, SWIFT; on-chain payout on CRYPTO |
| Payouts to China | Stablecoin wallets | On-chain deposit | Quote-driven CHINA_OFFRAMP payout to a China bank account |
Corridor availability depends on your entitlements. Call GET /v1/payments/providers for the corridor identifiers enabled for your integration; the examples below use provider-a, provider-b, and provider-c as placeholders.
Individual Payout Limits is documented alongside them. It is not a corridor but a follow-on flow: once an individual customer is onboarded, read their payout limits and request higher ones.
Core Concepts
Object Hierarchy
- Customer — the legal entity, business or natural person, on whose behalf accounts and payments are managed.
- Account — a payment account for a customer, one per currency or asset. Provisioned automatically when onboarding completes.
- Beneficiary — a bank account or on-chain wallet that can receive payouts.
- Deposit — an inbound credit to an account. Credited when it reaches
COMPLETED. - Quote — a time-boxed exchange rate between two currencies. Backs conversions and quote-driven payouts.
- Conversion — execution of a quote that settles into an account balance, independent of any later payout.
- Withdrawal — an outbound payment from an account to a beneficiary.
- Transaction — the unified view of deposits, withdrawals, and conversions.
- Limit request — a request to raise an individual customer's payout limits, reviewed asynchronously.
Base URLs
Build against sandbox first, then switch to production.
| Environment | Base URL |
|---|---|
| Sandbox | https://api-staging.checker.finance |
| Production | https://api.checker.finance |
All endpoints sit under the base path /v1/payments.
Authentication
Every request is signed with your API secret using HMAC-SHA256.
| Header | Description |
|---|---|
X-Api-Key | Your API key. |
X-Timestamp | Current UTC time, RFC 3339 (e.g. 2026-10-05T14:03:00Z). Must be within 5 minutes of server time. |
X-Nonce | A fresh random value per request (e.g. a UUID v4). Not signed. |
X-Signature | Lowercase hex HMAC-SHA256 of the string to sign, keyed with your API secret. |
String to sign: five lines joined with \n, no trailing newline.
METHOD
PATH
QUERY
TIMESTAMP
BODYMETHOD: upper case.PATH: path only, e.g./v1/payments/accounts.QUERY: query string without?, empty if none. Encode it exactly as sent: spaces as+, other reserved characters as upper-case%XX.TIMESTAMP: the exactX-Timestampvalue.BODY: the exact request body bytes. Empty forGETand formultipart/form-datauploads.- Use the secret as-is (UTF-8). Do not decode it.
Test vector — secret sbx_secret_example_do_not_use, GET /v1/payments/accounts?limit=10, timestamp 2026-10-05T14:03:00Z, no body:
77f5c357e886cb42fd92efecd2218b31ad8cd76deba54d20eb58e5bc01f73557import hashlib, hmac
def sign(secret: str, method: str, path: str, query: str, timestamp: str, body: bytes = b"") -> str:
msg = f"{method}\n{path}\n{query}\n{timestamp}\n".encode() + body
return hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()import crypto from "node:crypto";
const sign = (secret, method, path, query, timestamp, body = "") =>
crypto.createHmac("sha256", secret)
.update(`${method}\n${path}\n${query}\n${timestamp}\n${body}`)
.digest("hex");func sign(secret, method, path, query, timestamp string, body []byte) string {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(method + "\n" + path + "\n" + query + "\n" + timestamp + "\n"))
mac.Write(body)
return hex.EncodeToString(mac.Sum(nil))
}Serialize the JSON body once and send those exact bytes. A 401 almost always means the signed path, query, body, or timestamp differs from what was sent.
Idempotency
Every POST and PATCH request requires an Idempotency-Key header. Replaying the same key with the same body returns the original response instead of creating a duplicate, so retries after a network failure are safe.
When retrying, reuse the Idempotency-Key but send a new timestamp, nonce, and signature.
Asynchronous Operations
Onboarding, account provisioning, conversions, deposits, and withdrawals settle asynchronously. These endpoints return an initial non-terminal status; retrieve the resource with a GET request until it reaches a terminal state.
| Resource | Initial Status | Terminal Statuses |
|---|---|---|
| Customer | PENDING | ACTIVE (after first onboard), INACTIVE |
| Onboarding | PENDING | ACTIVE, FAILED |
| Account | PENDING | ACTIVE, FAILED, SUSPENDED, CLOSED |
| Deposit | PENDING / SUBMITTED / PROCESSING | COMPLETED, FAILED, CANCELLED |
| Conversion | PENDING | COMPLETED, FAILED |
| Withdrawal | PENDING / SUBMITTED / PROCESSING | COMPLETED, FAILED, CANCELLED |
| Limit request | REQUESTED | APPROVED, REJECTED |
A FAILED or CANCELLED deposit or withdrawal may carry a reason_code. Treat that list as open and surface an unrecognised value verbatim.
Error Responses
Errors use a consistent shape. Branch on error_code, which is stable and machine-readable. The message is safe to surface to end users.
{
"error_code": "VALIDATION_ERROR",
"message": "currency is required"
}Build with AI
- Docs index:
https://docs.checker.finance/llms.txt. Append.mdto any docs URL for plain markdown. - Docs MCP server:
https://docs.checker.finance/mcpgives AI assistants endpoint and schema lookup.
{ "mcpServers": { "checker-docs": { "url": "https://docs.checker.finance/mcp" } } }Add the snippet to ~/.cursor/mcp.json (Cursor), or run claude mcp add --transport http checker-docs https://docs.checker.finance/mcp (Claude Code).
Updated 3 days ago
