Integration Checklist
The path from the first request to go-live, in four phases. Each item is a question to answer before moving on, with a pointer to the page that has the details. Coverage says what the API supports; this page says in what order to use it.
What You Are Building
Your product lets your users exchange stablecoins for euros, or euros for stablecoins, without leaving your application. Stablegate provides the regulated entity, customer verification and screening, bank and blockchain settlement, and the order lifecycle. Your side owns the user: sign-up, the verification hand-off, the screen with deposit instructions, and the status the user sees afterwards.
From the user's point of view: sign up in your app, pass verification once, add the account the money should go to, see a quote, send the deposit, receive the payout.
Phase 1: Prepare
- Which flows do you need?
On-ramp, off-ramp or both. Confirm the pairs withGET /v1/exchange-routes/; the API exchanges only what that endpoint returns. - Who are your customers?
Individuals can use Instant Verification and trade within minutes; corporate customers go through the hosted Standard flow and activation. Ask your account manager to enable Instant Verification if you plan to use it. - Where does the payout go?
To a bank account or wallet registered for the customer. If your product needs anything else, agree it with Stablegate before you build. - Do you have access?
Partner API access shows API keys, Webhooks and Your clients in the Developers section; without it every call answers403. See Getting Started. - Does the smoke test pass?
GET /v1/exchange-routes/from your backend answers200withX-Api-Keyand a trailing slash on the path. A missing slash answers307with a redirect to the slashed path instead of the data. See Authentication. - Is your client generated from the OpenAPI specification?
Required fields are validated by the API; optional fields are not, so a typo in an optional field name silently falls back to the default.
Phase 2: Build
Customers and verification (Customers)
- Do you store the customer
uuidand your ownexternal_reference?
Create the customer when the user enters the exchange feature and keep the mapping; a customer cannot be edited later. - Do you start verification explicitly?
It never starts on its own. Instant Verification needsfirst_name,last_nameanddate_of_birth; the Standard link is valid for 30 minutes. - Do you gate trading on
state: activeandpermissions.can_trade: true?
PollGET /v1/customers/{customer_uuid}/after submission.kyc_levelalone is not enough for a Standard customer, who also needs activation.
Accounts (External Accounts)
- Is the payout account registered before the first order?
A bank account for off-ramp, a wallet for on-ramp. Keep theuuidto pass asdestination.client_account_uuid. - Do you collect the wallet ownership proof at registration time?
Ethereum wallets need the signed message, with a timestamp less than 24 hours old. A duplicate address answers400oa_client_external_account_address_exists; ask the user for another wallet.
- Do you run the three checks before each order?
POST /v1/exchange-routes/check/answersavailable: true;check-feesgives the quote you show as an estimate;check-limitsanswers an empty list. Any item in that list means refusal. - Do you send an
Idempotency-Key?
Generate it before the first attempt and reuse the same key only when retrying the same request. Retry network errors and5xx; do not retry4xxexcept409. See Idempotency. - Is the deposit screen rendered from the order you just created?
On-ramp:deposit_bank_infowithholder_nameas the payee and thereferencethe user must copy exactly. Off-ramp:deposit_address, the network andsource.amount. Both showvalid_untilas the deadline anddestination.amountas an estimate until the deposit lands. - Is there one open order per customer and destination?
Crypto deposits are matched by address and attach to the newest open order. See How Deposits Are Matched.
Monitoring (Webhooks)
- Does your webhook endpoint handle deliveries correctly?
VerifyX-Api-Sigover the raw body before parsing, answer2xxwithin 60 seconds, and ignore event types you do not know. - Do you deduplicate and order events yourself?
Deduplicate onorder_uuidplusstatusand order by your receipt time; the payloadtimestampis the order creation time. - Do you poll open orders as a fallback?
A delivery that fails its retries is not replayed. Whilestateisnew, the same poll readssource.deposit_status. - Is
failednon-terminal in your status model?
Final states aredone,rejected,cancelled,stoppedandrollbacked.
Phase 3: Test
- Has one on-ramp and one off-ramp order reached
done?
End to end, with a real deposit. - Have the negative paths run?
A route that is unavailable, a limit that is exceeded, an order left to expire pastvalid_until, a cancelled order, a webhook endpoint that was down during a status change and caught up by polling. - Do your logs keep
trace_idfrom every error response?
Support asks for it first.
Phase 4: Go Live
- Are support contacts exchanged in both directions?
The cases that need them: a deposit without reference, a transfer on the wrong network, a rejected deposit, a customer that needs closing or unblocking. - Is key rotation rehearsed?
Create a new key, switch, delete the old one; deletion takes effect immediately. - Do you follow changes?
The Changelog lists every change, and Stablegate sends each partner a personal digest of what affects their integration.
Common Mistakes
| Mistake | What happens | What to do instead |
|---|---|---|
| Path without the trailing slash | 307 and an extra round trip, or no data for a client that does not follow redirects | End every path with / |
Treating check-fees as a locked quote | The payout differs from what the user saw | Show the quote as an estimate; read exchange_rate and rate_valid_until from the order |
Reading excess_amount > 0 as "limit exceeded" | A breached minimum-amount rule is missed | Any item in the check-limits response means refusal |
| Waiting for a webhook for verification or deposit | Nothing arrives | Poll the customer detail for can_trade, and the order detail for deposit_status |
| Creating a second order while one is open on the same address | The deposit attaches to the newest order | One open order per customer and destination |
| Re-serializing the webhook body before verifying the signature | Every signature check fails | Verify over the raw bytes |
Ordering events by the webhook timestamp | Transitions appear out of order | Use your receipt time and the previous_status to status pair |
Treating failed as final | The order later moves to processing or done and your user sees a wrong status | Keep polling until a final state |
Treating 404 on a customer UUID as an auth problem | Time lost on key checks | 404 means not your customer; 403 means the key lacks access |
| Parsing amounts as floats | Rounding errors and refused orders | Use a decimal type and fraction_digits |
Ready to integrate?
Overview
End-to-end walkthroughs of the on-ramp (fiat to crypto) and off-ramp (crypto to fiat) flows with request and response examples.
On-Ramp Guide
This guide walks you through a complete on-ramp flow — converting EUR to USDC via the Stablegate API. By the end, your customer will have USDC delivered to their Ethereum wallet.