Skip to main content
After you create an offramp order, you track it from the stablecoin deposit through to the fiat payout arriving in your user’s bank account. This guide shows you how to monitor that journey, react to each status correctly, and handle every outcome along the way. In this guide, you will learn how to:
  • Check an order’s status and know which fields to watch
  • Follow an order through each phase of its lifecycle
  • Map each status to the right UI state and next action
  • Receive order updates through webhooks instead of polling
  • Troubleshoot expired quotes and rejected payouts

Check order status

Fetch an order any time with its orderId (see the Get Order API reference for the complete response schema):
curl -X GET \
  'https://staging.crossmint.com/api/2022-06-09/orders/<ORDER_ID>' \
  -H 'X-API-KEY: <YOUR_SERVER_API_KEY>'
The response includes the full order object (see the Quickstart for the full end-to-end flow). The key fields to watch are:
  • phase: the high-level stage of the order
  • payment.status: whether the stablecoin deposit has been received
  • lineItems[0].delivery.status: whether the fiat payout is pending, in transit, completed, or failed
If you poll instead of using webhooks, use the status-handling reference below to decide when to stop and what to show next. Below is a full completed-order response with the key fields highlighted:
{
  "orderId": "d3d1e4d8-1aae-45f1-804a-4ca674215bae",
  "phase": "completed",
  "locale": "en-US",
  "lineItems": [
    {
      "metadata": {
        "name": "USD",
        "description": "United States Dollar",
        "imageUrl": "https://www.crossmint.com/assets/ui/flags/us.svg"
      },
      "quote": {
        "status": "expired",
        "charges": {
          "unit": { "amount": "1", "currency": "usdc" },
          "fees": { "type": "exact", "amount": "0.002", "currency": "usdc" }
        },
        "totalPrice": { "amount": "1", "currency": "usdc" },
        "quantityRange": { "lowerBound": "1", "upperBound": "1" }
      },
      "delivery": {
        "status": "completed",
        "recipient": {
          "locator": "paymentMethodId:fb04b8bb-2bba-47ba-a105-66a93f61d468",
          "paymentMethodId": "fb04b8bb-2bba-47ba-a105-66a93f61d468"
        }
      },
      "executionMode": "exact-in",
      "callData": {
        "mode": "exact-in",
        "amount": "1",
        "outFiatCurrency": "usd",
        "effectiveAmount": "0.998"
      },
      "quantity": 1
    }
  ],
  "quote": {
    "status": "expired",
    "quotedAt": "2026-06-18T15:47:51.931Z",
    "expiresAt": "2026-06-18T15:57:51.931Z"
  },
  "payment": {
    "status": "completed",
    "method": "base",
    "currency": "usdc",
    "preparation": {
      "chain": "base",
      "payerAddress": "0x64EC529c6525CeD1Bd565a954a725787E31a89b9"
    },
    "received": {
      "chain": "base",
      "txId": "0xc40255de1c9028d1c6401249f5f4a2815749b284f2325d94d231808c2633b3d8",
      "amount": "1",
      "currency": "usdc"
    },
    "receiptEmail": "user@example.com"
  },
  "legal": {
    "requirements": [
      {
        "display": "show-checkbox",
        "type": "crossmint-terms-of-service",
        "url": "https://www.crossmint.com/legal/crossmint-terms-of-service/TRGUSAALLALLALL"
      },
      {
        "display": "show-text",
        "type": "crossmint-privacy-policy",
        "url": "https://www.crossmint.com/legal/crossmint-privacy-policy/ALLALLALLALLALL"
      }
    ]
  }
}
A completed order can still show quote.status: "expired" because the quote time window closes independently of payment and delivery. By itself, this does not indicate a problem. Rely on phase, payment.status, and lineItems[].delivery.status to determine the order outcome.

Order lifecycle

The full status model has four phases. Offramp orders start in payment, with the quote included inside the order, then move through delivery to completed when the payout reaches a terminal outcome. The quote phase appears if the quote expires before the deposit arrives.
Offramp order lifecycle showing the happy path (Order created, Deposit received, Payout in progress, Payout completed) and failure paths (quote expired before deposit, payout failed)
1

Payment: waiting for the deposit

After the order is created and the quote is locked, the order sits in the payment phase until the stablecoin deposit arrives on-chain and is matched by memo.
{
  "phase": "payment",
  "payment": { "status": "awaiting-payment" },
  "lineItems": [{ "delivery": { "status": "awaiting-payment" } }]
}
If no deposit arrives before the quote expires, the order returns to the quote phase with payment.status: "requires-quote". See Quote expired below.
2

Delivery: fiat payout in progress

Once the deposit lands, Crossmint converts the stablecoin and initiates the fiat payout. The order moves to the delivery phase.
{
  "phase": "delivery",
  "payment": {
    "status": "completed",
    "received": {
      "chain": "base-sepolia",
      "txId": "0xc953...a338e85",
      "amount": "1",
      "currency": "usdc"
    }
  },
  "lineItems": [{ "delivery": { "status": "in-progress" } }]
}
Offramp settlement has two asynchronous parts: receiving the stablecoin deposit and completing the fiat payout. After payment.status becomes completed, the order may remain in delivery while the payout is being prepared, processed by the rail, or delayed by weekends and bank holidays. For rail-specific timing, see Payment Schemes.
3

Completed: terminal state

Once the bank transfer completes, the order moves to completed with a successful delivery status.
{
  "phase": "completed",
  "payment": {
    "status": "completed",
    "received": {
      "chain": "base-sepolia",
      "txId": "0xc953...a338e85",
      "amount": "1",
      "currency": "usdc"
    }
  },
  "lineItems": [{ "delivery": { "status": "completed" } }]
}
phase: "completed" means the order is terminal, not necessarily successful. Always check lineItems[0].delivery.status to determine the outcome: "completed" (success) or "failed" (see Bank rejection).

Order status reference

Each field below returns one of a fixed set of values. Expand a field for the full list.
ValueMeaning
quoteQuote pending or expired; no deposit has been taken yet
paymentQuote locked; waiting for or processing the on-chain stablecoin deposit
deliveryStablecoin deposit received; fiat payout is pending or in progress
completedTerminal. Check delivery.status for the outcome
In the standard offramp flow, payment.status typically moves through awaiting-payment, in-progress, and completed. The other statuses listed below represent missing setup steps, verification requirements, or payer-wallet conditions.
ValueMeaning
requires-quoteA new or refreshed quote is needed (for example, the previous quote expired)
requires-crypto-payer-addressThe payer wallet address has not been set yet
requires-emailA receipt email is required before payment can proceed
requires-kycIdentity verification (KYC) is required before payment can proceed
pending-kyc-reviewKYC submission is under review
manual-kycKYC requires manual review
failed-kycKYC verification was rejected
requires-recipient-verificationThe recipient must complete wallet ownership verification
awaiting-deposit-instructionsDeposit instructions are being prepared; wait until the order returns awaiting-payment before asking the user to send the deposit
awaiting-paymentQuote accepted; waiting for the user to send the stablecoin deposit
crypto-payer-insufficient-fundsThe payer wallet does not have enough funds for the deposit
crypto-payer-insufficient-funds-for-gasThe payer wallet does not have enough native token to pay network gas
in-progressStablecoin deposit detected; waiting for on-chain confirmations
completedStablecoin deposit received and matched to the order
ValueMeaning
awaiting-paymentFiat payout not yet initiated
in-progressFiat payout initiated; awaiting rail completion
completedFiat payout completed
failedPayout could not be completed (for example, bank rejected the transfer)
When a deposit cannot be processed or a payout fails, the order response may include a structured failure reason:
  • payment.failureReason: may be present when the deposit cannot be processed. Shape: { code, message?, developerMessage? }.
  • lineItems[].delivery.failureReason: may be present when the fiat payout fails. Shape: { code, message? }. Possible codes: transfer-failed, slippage-tolerance-exceeded, payment-refunded.
Payment failure reasons are returned on the order object. If you need to detect deposit-processing issues before a delivery event fires, refetch the order with the Get Order API and inspect payment.status and payment.failureReason.

Status-handling reference

Use this table to decide what to show the user and what action to take:
phasepayment.statusdelivery.statusMeaningDeveloper action
paymentawaiting-paymentawaiting-paymentWaiting for stablecoin depositShow deposit instructions
paymentin-progressawaiting-paymentStablecoin deposit detected; waiting for on-chain confirmationsShow “Payment confirming”; do not ask the user to resend funds
deliverycompletedawaiting-paymentStablecoin deposit received; fiat payout not yet initiatedShow “Payout pending”; do not ask the user to resend funds
deliverycompletedin-progressStablecoin deposit received; fiat payout awaiting rail completionShow “Payout on the way”; do not ask the user to resend funds
completedcompletedcompletedPayout completed successfullyShow “Funds sent” confirmation
completedcompletedfailedPayout failedCheck payment.refunded for refund details; prompt user to retry with corrected bank details
quoterequires-quoteawaiting-paymentQuote expired before depositCreate a new order with a fresh quote
These statuses may appear during order setup or when the payer wallet has issues. Use payment.status as the key to decide what to show:
payment.statusMeaningDeveloper action
requires-crypto-payer-addressPayer wallet address is missingProvide the payer wallet address, then refetch the order
requires-emailReceipt email is requiredCollect the email, then refetch the order
requires-kycIdentity verification is requiredSend the user through verification before showing deposit instructions
pending-kyc-review, manual-kycIdentity verification is under reviewShow verification pending and wait for the status to update
failed-kycIdentity verification was rejectedDo not ask the user to send funds; show the verification failure state
requires-recipient-verificationWallet ownership verification is requiredComplete recipient verification, then refetch the order
awaiting-deposit-instructionsDeposit instructions are being preparedWait until the order returns awaiting-payment before showing deposit instructions
crypto-payer-insufficient-fundsPayer wallet does not have enough stablecoin for the depositAsk the user to fund the wallet or use another wallet
crypto-payer-insufficient-funds-for-gasPayer wallet does not have enough native token for gasAsk the user to add gas or use a wallet with gas sponsorship

Webhooks

Instead of polling, subscribe to order events so Crossmint notifies your backend as the order progresses. Configure an endpoint in the Crossmint console and subscribe to events using the Webhooks guide. The events relevant to the standard offramp progression are:
EventFires when
orders.payment.succeededThe on-chain stablecoin deposit is matched to the order
orders.delivery.initiatedThe fiat payout has been initiated
orders.delivery.completedThe fiat payout has completed
orders.delivery.failedThe payout could not be completed
Use these events to drive your UI updates. For example, show the user “payout on the way” when you receive orders.delivery.initiated and “funds sent” when you receive orders.delivery.completed.

Troubleshooting

An offramp order spans two independent systems: an on-chain stablecoin deposit and a fiat payout over banking rails. Most orders complete on their own, but a few situations can interrupt the standard flow, most often a quote that expires before the deposit arrives or a payout the bank declines. Both are expected and recoverable. Expand a case below to see how to detect and resolve it.
If the user does not send the stablecoin deposit before the quote expires, the order returns to the quote phase. No funds are collected.
{
  "phase": "quote",
  "quote": { "status": "expired" },
  "payment": { "status": "requires-quote" },
  "lineItems": [{ "delivery": { "status": "awaiting-payment" } }]
}
The exchange rate is stale. Create a new order to get a fresh quote.
If the bank rejects the payout after the stablecoin deposit has been received (for example, invalid account details), the order moves to completed with a failed delivery. The buyer deposit is automatically refunded.
{
  "phase": "completed",
  "payment": {
    "status": "completed",
    "refunded": { "amount": "1", "currency": "usdc" }
  },
  "lineItems": [
    {
      "delivery": {
        "status": "failed",
        "failureReason": { "code": "transfer-failed" }
      }
    }
  ]
}
Check payment.refunded to confirm the refund details. Listen for orders.delivery.failed so you can surface the issue in your app and prompt the user to try again with corrected bank details.

Next steps

Quickstart

Build the full flow

Launch in Production

Move from staging to live payouts