Skip to main content
The API is the same set of rails the widget runs on, called from your backend, with your own UI on top. It covers both directions money comes in - stablecoin from a wallet, fiat from a card or a bank - behind one key and one error format. This page is both the catalogue and the walkthrough: which products the API exposes, then the order of calls, where the key lives, and what your backend has to do. Two things hold across every product:
  • Non-custodial. We never hold the funds and never sign: the transaction we return is unsigned and your user’s wallet signs it. Nothing sits with us between the payer and the merchant.
  • Stateless. There are no server-side sessions. Each call is keyed by the ids the previous call returned (orderId, provider, payload, txHash, transactionId, accountId). Your client holds the thread.

Which products it covers

Card issuing is documented separately - see Card issuing. Reference lookups cover all of them: read the supported countries, currencies, limits, rates, and statuses instead of hard-coding them. Full request and response shapes are in the API reference.

Authentication

Every call uses an API key against the /v1 prefix. There is one kind of key: it carries its products, corridors and rate, and works from your server or a page alike, so keep it where only you can read it. What a key is allowed to settle - which chains and tokens as a destination - is part of your account setup, not something you pass per request. The header and what a refusal looks like are on Authentication.

What your key carries

Read your own setup rather than asking us for it. GET /v1/config answers it, narrowed to your key: the products you may call, your maxExecutionUsd, and under crypto the chains and tokens you may name. Call it first and build your corridor list from it instead of keeping a copy. A product not on your key is refused, never defaulted: its endpoints answer 403 permission_denied naming the product. To use a product that is not on your key yet, ask us to add it - one configuration change on our side. Source and destination are set separately on purpose: taking payment from a chain and settling on it are different things, and you rarely want both everywhere. A quote names both sides in full - destination.chain and destination.token are required, and a chain your key is not enabled for is refused with 403 chain_not_allowed.
These corridors describe your account setup, not a security boundary. They keep your integration inside what we agreed and give you a clear error when a request falls outside it. If you need something locked down, lock it down on your side too - we will happily match it here, but do not rely on this as your only control.

Read the lists before you call anything

The reference lists come first in the API reference because you need them before the first call: which countries can be onboarded, which chains, tokens and fiat currencies exist, what every status means and which one to credit on, and the per-rail limits. Start at Countries and read them from the API rather than keeping a copy.

Which environment answers

Every response starts with environment: sandbox or production, derived from the upstreams this deployment is configured against. The products do not share one: crypto deposits run on production with real money, cards and bank transfers run on the providers’ sandboxes. Each product page in this documentation and each endpoint page in the reference says which applies to it.

The flow

The walkthrough below is the crypto deposit path - the same three calls the widget makes, on the same rails. The other products follow the same shape: price it, start it, track it. Their calls are on the product pages linked above.
1

Price it

POST /v1/deposit/quote with where the money comes from, where it should land, and the amount.Decide which side the amount pins down. exact-in means the payer spends exactly that and whatever arrives, arrives. target-out (exact out) means a fixed figure must land and the payer’s side flexes to cover it - this is the one you want when you are collecting a set price.See Quote a deposit.
2

Build the transaction

POST /v1/deposit/transaction with the quote you got back. You receive an unsigned transaction, and an approval transaction before it when the token allowance is not enough.See Build the transaction.
3

Let the user sign

Your frontend submits it from the user’s wallet. Nothing reaches us here.
4

Track it

POST /v1/deposit/status with the receipt from the submitted transaction. Poll it until it is done, or take the webhook.See Check status.

Quotes

A quote prices the route and reserves liquidity, so it is only good for a short window and the window is short on purpose. Two things follow for your UI:
  • Show the user how long is left, and give them a way to ask for a fresh quote.
  • Do not build a transaction from a quote you have been sitting on. Re-quote and build from the new one.
Quotes work in both directions. Fix what the payer spends and whatever arrives arrives, or fix what has to land and let the payer’s side flex to cover it - the second is what you want when collecting a set price. How amounts are expressed on either side is on Amounts and currencies.

Naming the destination

Name both destination.chain and destination.token on the quote - a chain without its token address is refused rather than guessed. Read the chains and tokens your key may use from GET /v1/config. Your key has to be enabled for that chain as a destination. If it is not, you get 403 chain_not_allowed rather than a quote you could not have used. That is your account setup talking, not a security control - see What your key carries.

Payment methods

What a user can pay with, per product:
  • Stablecoins from a connected wallet, on any supported network - see Coverage.
  • Cards, priced before the user commits to anything.
  • Bank transfers into a virtual account: local rails and international wires, depending on the currency. The per-currency list is on Coverage and served by GET /v1/currencies.

Statuses

Each product has its own small status set: deposits report pending, done, failed, or expired; cards report pending, processing, completed, failed, or unknown; bank transfers report awaiting_deposit or funded. In every set there is exactly one state that means “credit the user” - done, completed, funded - and you credit on that state only. A partner state we do not recognise is reported as unknown and flagged, never quietly shown as “in progress”. See Transaction statuses.

Rate limits

Your key carries a request ceiling, agreed when it is issued and counted per key, never per IP: every request from you reaches us from your servers, so we see one caller, not your users. Over the limit returns 429 rate_limited. Design polling and retries to stay under it - for status polling, a few seconds between calls is plenty - and handle 429 by backing off rather than retrying immediately. Limiting your own users is yours to do. You know who they are - they are logged in to your product - and we do not. If one of them hammers your integration, they will spend your ceiling and your other users will feel it. If you are onboarding in bursts, tell us and we will raise your ceiling rather than let you discover it in production.

Amount limits

Amount limits are per rail and per provider. The stablecoin path is bounded by live liquidity on the route, which is why a quote can come back as no route available - that is a normal, temporary answer, not an error to retry in a loop. Fiat minimums and maximums are per currency and rail - read them from GET /v1/limits.

Errors

Every error comes back in the same envelope, with a code to branch on and a message for your logs. The envelope, the full code table, and the one exception (bank transfer and virtual account endpoints pass the banking partner’s own codes through unchanged) are on Errors.

Before you go live

Handle 403 chain_not_allowed - it means the corridor was never yours, so retrying will not help.
Handle 429 by backing off, not by retrying immediately.
Treat no_route as a normal, temporary answer - liquidity moves.
Keep the key on your server and out of anything a browser can read.