Everything live today is a test network. Nothing is deployed to any mainnet yet, so every payment you create through this API settles in testnet money. Every example on these pages uses Base Sepolia, chain ID
84532. Roadmap.Base URL
Every endpoint lives under one base URL. It is our shared edge-function host — the same host for every merchant, not a value issued per account. The API & SDK tab in the dashboard gives you your API key and the embed snippet. It does not list the base URL today, and the webhook signing secret shown in Settings → Webhooks is a read-only field that nothing provisions yet — ask us for either rather than guessing. Webhooks. Every example on these pages uses an environment variable for it:POST and accept and return JSON.
Authentication
There is no bearer token on these endpoints. They are public by design — the customer’s browser calls them directly — so what gates them is your origin allowlist and the identifiers in the body, not a secret in a header.string
required
The origin making the call. It must be on your account’s allowed-origins list, or the request is refused with
403. Browsers set this automatically; a server-side caller has to send it explicitly.string
required
Identifies your account. On the payment endpoints the
quoteId carries it forward.string
required
application/jsonExamples elsewhere in this reference also send an
apikey header, as $CC_PUBLISHABLE_KEY. That is the project key our browser SDK sends; these endpoints accept the call with or without it, and it does not identify your account. The allowed-origins list is the control, not that header.The API key on the API & SDK tab is a different thing. The
sk_live_… value there is a real secret, but none of the endpoints documented here read it — it is sent as X-Api-Key (that header name, matched case-insensitively; never apikey) to a separate server-to-server session endpoint that the current checkout does not use. Keep it on your server, and don’t reach for it to authenticate the calls above.Conventions
Amounts are base units
Amounts are base units
Token amounts on the payment and status endpoints —
expectedAmount, confirmedAmount — are strings in the token’s smallest unit. On most chains USDC, USDT, and EURC use 6 decimals, so "49000000" is 49.00.The quote is the exception. stablecoinAmount from create-quote is a plain decimal string, e.g. "53.165". Convert it yourself if you need base units — creating the payment does that conversion for you.Coming Quoted amounts will be rounded to two decimals at the moment the quote is made, which changes the value this endpoint returns — a figure like the three-decimal one above will come back with two. Read the amount the API gives you and pass it through; don’t re-derive it from your cart total or round it again on your side, or your figure and ours will disagree. It will be announced before it ships. Roadmap.Do not hardcode 6. Decimals vary by chain — USDT on BNB Chain mainnet is 18-decimal, though every network live today settles 6-decimal tokens. Use the tokenDecimals field returned with the payment.Strings, not numbers — a large amount would lose precision as a float.Fiat amounts are decimals
Fiat amounts are decimals
Cart totals are ordinary decimal numbers:
49.00.Chains are numeric IDs
Chains are numeric IDs
The IDs you can quote against today are
84532 Base Sepolia, 11155111 Ethereum Sepolia, 421614 Arbitrum Sepolia, 11155420 OP Sepolia, 80002 Polygon Amoy, 43113 Avalanche Fuji, 97 BNB testnet, and TRON Nile. Which of them you can use depends on your account: not every token exists on every one. Chains and tokens.The mainnet IDs each network will use once it opens — 1 Ethereum, 8453 Base, 42161 Arbitrum, 10 Optimism, 137 Polygon, 56 BNB, 43114 Avalanche — are not usable today. Quote one and you get 422 with chain_not_activated.A quote is only accepted for a chain activated on your account, so a test network you haven’t enabled returns that same 422.Timestamps are ISO 8601
Timestamps are ISO 8601
UTC, but the exact shape varies: values come back with a
+00:00 offset rather than a trailing Z, and some carry microsecond precision — 2026-08-26T06:07:33.083+00:00, 2026-08-26T06:02:33.186021+00:00.Parse with a real ISO 8601 parser. Don’t match on a trailing Z or assume milliseconds.Address casing is not uniform
Address casing is not uniform
EVM casing varies by field: your pool address comes back checksummed, a one-time deposit address comes back lowercase. Compare EVM addresses case-insensitively and never rely on the casing you were served.TRON addresses are base58 starting with
T and are case-sensitive — never normalise them.Typical flow
Create a quote
Locks a price for a short window and produces a payment ID. create-quote.
Create a payment
Produces either a pool address to sign against, or a one-time deposit address. create-payment.
Wait for confirmation
Use webhooks — fulfil at capture:
pool.deposit.confirmed when the customer sent a plain transfer, pool.swept when they paid from a connected wallet. Poll the status endpoint only as a backstop.Rate limits
Rate limited per calling IP address, in fixed one-minute windows — not per account. Quoting and payment creation allow roughly 30 requests a minute from one IP; status polling allows roughly 120. Exceeding either returns429 with a Retry-After header — respect it and back off.
Because the limit is per IP, a server-side integration puts all of your traffic behind one bucket. Quote when a customer reaches checkout rather than on every page view.
Do not build these yourself
Unless you have a specific reason, embed the checkout instead.Errors
Every error code and what to do about it.