---
title: "Integration Checklist"
description: "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."
canonical_url: "https://docs.stablegate.com/docs/guide/integration-guides/checklist"
---
# 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.

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](https://docs.stablegate.com/docs/guide/getting-started/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?**:brOn-ramp, off-ramp or both. Confirm the pairs with [`GET /v1/exchange-routes/`](https://docs.stablegate.com/docs/guide/exchange-routes); the API exchanges only what that endpoint returns.
- [ ] **Who are your customers?**:brIndividuals can use [Instant Verification](https://docs.stablegate.com/docs/guide/customers#start-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?**:brTo 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?**:brPartner API access shows **API keys**, **Webhooks** and **Your clients** in the Developers section; without it every call answers `403`. See [Getting Started](https://docs.stablegate.com/docs/guide/getting-started).
- [ ] **Does the smoke test pass?**:br`GET /v1/exchange-routes/` from your backend answers `200` with `X-Api-Key` and a trailing slash on the path. A missing slash answers `307` with a redirect to the slashed path instead of the data. See [Authentication](https://docs.stablegate.com/docs/guide/getting-started/authentication).
- [ ] **Is your client generated from the OpenAPI specification?**:brRequired 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](https://docs.stablegate.com/docs/guide/customers))

- [ ] **Do you store the customer uuid and your own external_reference?**:brCreate the customer when the user enters the exchange feature and keep the mapping; a customer cannot be edited later.
- [ ] **Do you start verification explicitly?**:brIt never starts on its own. Instant Verification needs `first_name`, `last_name` and `date_of_birth`; the Standard link is valid for **30 minutes**.
- [ ] **Do you gate trading on state: active and permissions.can_trade: true?**:brPoll `GET /v1/customers/{customer_uuid}/` after submission. `kyc_level` alone is not enough for a Standard customer, who also needs activation.

**Accounts** ([External Accounts](https://docs.stablegate.com/docs/guide/external-accounts))

- [ ] **Is the payout account registered before the first order?**:brA bank account for off-ramp, a wallet for on-ramp. Keep the `uuid` to pass as `destination.client_account_uuid`.
- [ ] **Do you collect the wallet ownership proof at registration time?**:brEthereum wallets need the signed message, with a timestamp less than **24 hours** old. A duplicate address answers `400` `oa_client_external_account_address_exists`; ask the user for another wallet.

**Orders** ([Fees](https://docs.stablegate.com/docs/guide/fees-and-limits/fees), [Limits](https://docs.stablegate.com/docs/guide/fees-and-limits/limits), [Orders](https://docs.stablegate.com/docs/guide/orders))

- [ ] **Do you run the three checks before each order?**:br`POST /v1/exchange-routes/check/` answers `available: true`; `check-fees` gives the quote you show as an estimate; `check-limits` answers an empty list. Any item in that list means refusal.
- [ ] **Do you send an Idempotency-Key?**:brGenerate it before the first attempt and reuse the same key only when retrying the same request. Retry network errors and `5xx`; do not retry `4xx` except `409`. See [Idempotency](https://docs.stablegate.com/docs/guide/getting-started/idempotency).
- [ ] **Is the deposit screen rendered from the order you just created?**:brOn-ramp: `deposit_bank_info` with `holder_name` as the payee and the `reference` the user must copy exactly. Off-ramp: `deposit_address`, the network and `source.amount`. Both show `valid_until` as the deadline and `destination.amount` as an estimate until the deposit lands.
- [ ] **Is there one open order per customer and destination?**:brCrypto deposits are matched by address and attach to the newest open order. See [How Deposits Are Matched](https://docs.stablegate.com/docs/guide/orders#how-deposits-are-matched).

**Monitoring** ([Webhooks](https://docs.stablegate.com/docs/guide/webhooks))

- [ ] **Does your webhook endpoint handle deliveries correctly?**:brVerify `X-Api-Sig` over the raw body before parsing, answer `2xx` within **60 seconds**, and ignore event types you do not know.
- [ ] **Do you deduplicate and order events yourself?**:brDeduplicate on `order_uuid` plus `status` and order by your receipt time; the payload `timestamp` is the order creation time.
- [ ] **Do you poll open orders as a fallback?**:brA delivery that fails its retries is not replayed. While `state` is `new`, the same poll reads `source.deposit_status`.
- [ ] **Is failed non-terminal in your status model?**:brFinal states are `done`, `rejected`, `cancelled`, `stopped` and `rollbacked`.

## Phase 3: Test

- [ ] **Has one on-ramp and one off-ramp order reached done?**:brEnd to end, with a real deposit.
- [ ] **Have the negative paths run?**:brA route that is unavailable, a limit that is exceeded, an order left to expire past `valid_until`, a cancelled order, a webhook endpoint that was down during a status change and caught up by polling.
- [ ] **Do your logs keep trace_id from every error response?**:brSupport asks for it first.

## Phase 4: Go Live

- [ ] **Are support contacts exchanged in both directions?**:brThe 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?**:brCreate a new key, switch, delete the old one; deletion takes effect immediately.
- [ ] **Do you follow changes?**:brThe [Changelog](https://docs.stablegate.com/docs/changelog) lists every change, and Stablegate sends each partner a personal digest of what affects their integration.

## Common Mistakes

<table>
<thead>
  <tr>
    <th>
      Mistake
    </th>
    
    <th>
      What happens
    </th>
    
    <th>
      What to do instead
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Path without the trailing slash
    </td>
    
    <td>
      <code>
        307
      </code>
      
       and an extra round trip, or no data for a client that does not follow redirects
    </td>
    
    <td>
      End every path with <code>
        /
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Treating <code>
        check-fees
      </code>
      
       as a locked quote
    </td>
    
    <td>
      The payout differs from what the user saw
    </td>
    
    <td>
      Show the quote as an estimate; read <code>
        exchange_rate
      </code>
      
       and <code>
        rate_valid_until
      </code>
      
       from the order
    </td>
  </tr>
  
  <tr>
    <td>
      Reading <code>
        excess_amount > 0
      </code>
      
       as "limit exceeded"
    </td>
    
    <td>
      A breached minimum-amount rule is missed
    </td>
    
    <td>
      Any item in the <code>
        check-limits
      </code>
      
       response means refusal
    </td>
  </tr>
  
  <tr>
    <td>
      Waiting for a webhook for verification or deposit
    </td>
    
    <td>
      Nothing arrives
    </td>
    
    <td>
      Poll the customer detail for <code>
        can_trade
      </code>
      
      , and the order detail for <code>
        deposit_status
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      Creating a second order while one is open on the same address
    </td>
    
    <td>
      The deposit attaches to the newest order
    </td>
    
    <td>
      One open order per customer and destination
    </td>
  </tr>
  
  <tr>
    <td>
      Re-serializing the webhook body before verifying the signature
    </td>
    
    <td>
      Every signature check fails
    </td>
    
    <td>
      Verify over the raw bytes
    </td>
  </tr>
  
  <tr>
    <td>
      Ordering events by the webhook <code>
        timestamp
      </code>
    </td>
    
    <td>
      Transitions appear out of order
    </td>
    
    <td>
      Use your receipt time and the <code>
        previous_status
      </code>
      
       to <code>
        status
      </code>
      
       pair
    </td>
  </tr>
  
  <tr>
    <td>
      Treating <code>
        failed
      </code>
      
       as final
    </td>
    
    <td>
      The order later moves to <code>
        processing
      </code>
      
       or <code>
        done
      </code>
      
       and your user sees a wrong status
    </td>
    
    <td>
      Keep polling until a final state
    </td>
  </tr>
  
  <tr>
    <td>
      Treating <code>
        404
      </code>
      
       on a customer UUID as an auth problem
    </td>
    
    <td>
      Time lost on key checks
    </td>
    
    <td>
      <code>
        404
      </code>
      
       means not your customer; <code>
        403
      </code>
      
       means the key lacks access
    </td>
  </tr>
  
  <tr>
    <td>
      Parsing amounts as floats
    </td>
    
    <td>
      Rounding errors and refused orders
    </td>
    
    <td>
      Use a decimal type and <code>
        fraction_digits
      </code>
    </td>
  </tr>
</tbody>
</table>


## Sitemap

See the full [sitemap](https://docs.stablegate.com/sitemap.md) for all pages.
