> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crossmint.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Manage Orders

> Track offramp order status through every phase, handle webhooks, and troubleshoot common issues

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](/api-reference/headless/get-order) for the complete response schema):

```bash theme={null}
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](/offramp/quickstarts/rest-api) 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](#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:

```json {3,22,44} theme={null}
{
  "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"
      }
    ]
  }
}
```

<Note>
  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.
</Note>

## 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.

<Frame type="simple">
  <img src="https://mintcdn.com/crossmint/NDs-0yI_SrxGuRmv/images/offramp/order-lifecycle-swimlane.png?fit=max&auto=format&n=NDs-0yI_SrxGuRmv&q=85&s=502b553849b21af07cba6ea391098e9c" alt="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)" width="4515" height="1221" data-path="images/offramp/order-lifecycle-swimlane.png" />
</Frame>

<Steps>
  <Step title="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.

    ```json theme={null}
    {
      "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](#troubleshooting) below.
  </Step>

  <Step title="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.

    ```json theme={null}
    {
      "phase": "delivery",
      "payment": {
        "status": "completed",
        "received": {
          "chain": "base-sepolia",
          "txId": "0xc953...a338e85",
          "amount": "1",
          "currency": "usdc"
        }
      },
      "lineItems": [{ "delivery": { "status": "in-progress" } }]
    }
    ```

    <Note>
      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](/offramp/payment-methods).
    </Note>
  </Step>

  <Step title="Completed: terminal state">
    Once the bank transfer completes, the order moves to `completed` with a successful delivery status.

    ```json theme={null}
    {
      "phase": "completed",
      "payment": {
        "status": "completed",
        "received": {
          "chain": "base-sepolia",
          "txId": "0xc953...a338e85",
          "amount": "1",
          "currency": "usdc"
        }
      },
      "lineItems": [{ "delivery": { "status": "completed" } }]
    }
    ```
  </Step>
</Steps>

<Warning>
  `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](#troubleshooting)).
</Warning>

## Order status reference

Each field below returns one of a fixed set of values. Expand a field for the full list.

<AccordionGroup>
  <Accordion title="phase">
    | Value       | Meaning                                                                 |
    | :---------- | :---------------------------------------------------------------------- |
    | `quote`     | Quote pending or expired; no deposit has been taken yet                 |
    | `payment`   | Quote locked; waiting for or processing the on-chain stablecoin deposit |
    | `delivery`  | Stablecoin deposit received; fiat payout is pending or in progress      |
    | `completed` | Terminal. Check `delivery.status` for the outcome                       |
  </Accordion>

  <Accordion title="payment.status">
    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.

    | Value                                     | Meaning                                                                                                                             |
    | :---------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------- |
    | `requires-quote`                          | A new or refreshed quote is needed (for example, the previous quote expired)                                                        |
    | `requires-crypto-payer-address`           | The payer wallet address has not been set yet                                                                                       |
    | `requires-email`                          | A receipt email is required before payment can proceed                                                                              |
    | `requires-kyc`                            | Identity verification (KYC) is required before payment can proceed                                                                  |
    | `pending-kyc-review`                      | KYC submission is under review                                                                                                      |
    | `manual-kyc`                              | KYC requires manual review                                                                                                          |
    | `failed-kyc`                              | KYC verification was rejected                                                                                                       |
    | `requires-recipient-verification`         | The recipient must complete wallet ownership verification                                                                           |
    | `awaiting-deposit-instructions`           | Deposit instructions are being prepared; wait until the order returns `awaiting-payment` before asking the user to send the deposit |
    | `awaiting-payment`                        | Quote accepted; waiting for the user to send the stablecoin deposit                                                                 |
    | `crypto-payer-insufficient-funds`         | The payer wallet does not have enough funds for the deposit                                                                         |
    | `crypto-payer-insufficient-funds-for-gas` | The payer wallet does not have enough native token to pay network gas                                                               |
    | `in-progress`                             | Stablecoin deposit detected; waiting for on-chain confirmations                                                                     |
    | `completed`                               | Stablecoin deposit received and matched to the order                                                                                |
  </Accordion>

  <Accordion title="lineItems[0].delivery.status">
    | Value              | Meaning                                                                 |
    | :----------------- | :---------------------------------------------------------------------- |
    | `awaiting-payment` | Fiat payout not yet initiated                                           |
    | `in-progress`      | Fiat payout initiated; awaiting rail completion                         |
    | `completed`        | Fiat payout completed                                                   |
    | `failed`           | Payout could not be completed (for example, bank rejected the transfer) |
  </Accordion>

  <Accordion title="Failure reasons">
    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`.

    <Note>
      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](/api-reference/headless/get-order) and inspect `payment.status` and `payment.failureReason`.
    </Note>
  </Accordion>
</AccordionGroup>

## Status-handling reference

Use this table to decide what to show the user and what action to take:

| `phase`     | `payment.status`   | `delivery.status`  | Meaning                                                           | Developer action                                                                              |
| :---------- | :----------------- | :----------------- | :---------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- |
| `payment`   | `awaiting-payment` | `awaiting-payment` | Waiting for stablecoin deposit                                    | Show deposit instructions                                                                     |
| `payment`   | `in-progress`      | `awaiting-payment` | Stablecoin deposit detected; waiting for on-chain confirmations   | Show "Payment confirming"; do not ask the user to resend funds                                |
| `delivery`  | `completed`        | `awaiting-payment` | Stablecoin deposit received; fiat payout not yet initiated        | Show "Payout pending"; do not ask the user to resend funds                                    |
| `delivery`  | `completed`        | `in-progress`      | Stablecoin deposit received; fiat payout awaiting rail completion | Show "Payout on the way"; do not ask the user to resend funds                                 |
| `completed` | `completed`        | `completed`        | Payout completed successfully                                     | Show "Funds sent" confirmation                                                                |
| `completed` | `completed`        | `failed`           | Payout failed                                                     | Check `payment.refunded` for refund details; prompt user to retry with corrected bank details |
| `quote`     | `requires-quote`   | `awaiting-payment` | Quote expired before deposit                                      | Create a new order with a fresh quote                                                         |

<Accordion title="Less common payment statuses">
  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.status`                          | Meaning                                                      | Developer action                                                                    |
  | :---------------------------------------- | :----------------------------------------------------------- | :---------------------------------------------------------------------------------- |
  | `requires-crypto-payer-address`           | Payer wallet address is missing                              | Provide the payer wallet address, then refetch the order                            |
  | `requires-email`                          | Receipt email is required                                    | Collect the email, then refetch the order                                           |
  | `requires-kyc`                            | Identity verification is required                            | Send the user through verification before showing deposit instructions              |
  | `pending-kyc-review`, `manual-kyc`        | Identity verification is under review                        | Show verification pending and wait for the status to update                         |
  | `failed-kyc`                              | Identity verification was rejected                           | Do not ask the user to send funds; show the verification failure state              |
  | `requires-recipient-verification`         | Wallet ownership verification is required                    | Complete recipient verification, then refetch the order                             |
  | `awaiting-deposit-instructions`           | Deposit instructions are being prepared                      | Wait until the order returns `awaiting-payment` before showing deposit instructions |
  | `crypto-payer-insufficient-funds`         | Payer wallet does not have enough stablecoin for the deposit | Ask the user to fund the wallet or use another wallet                               |
  | `crypto-payer-insufficient-funds-for-gas` | Payer wallet does not have enough native token for gas       | Ask the user to add gas or use a wallet with gas sponsorship                        |
</Accordion>

## Webhooks

Instead of polling, subscribe to order events so Crossmint notifies your backend as the order progresses. Configure an endpoint in the <a href="https://www.crossmint.com/console" target="_blank">Crossmint console</a> and subscribe to events using the [Webhooks guide](/payments/advanced/webhooks).

The events relevant to the standard offramp progression are:

| Event                       | Fires when                                              |
| :-------------------------- | :------------------------------------------------------ |
| `orders.payment.succeeded`  | The on-chain stablecoin deposit is matched to the order |
| `orders.delivery.initiated` | The fiat payout has been initiated                      |
| `orders.delivery.completed` | The fiat payout has completed                           |
| `orders.delivery.failed`    | The 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.

<AccordionGroup>
  <Accordion title="Quote expired">
    If the user does not send the stablecoin deposit before the quote expires, the order returns to the `quote` phase. No funds are collected.

    ```json theme={null}
    {
      "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.
  </Accordion>

  <Accordion title="Bank rejection">
    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.

    ```json theme={null}
    {
      "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.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Quickstart" icon="bolt" href="/offramp/quickstarts/rest-api">
    Build the full flow
  </Card>

  <Card title="Launch in Production" icon="rocket" href="/offramp/guides/going-to-production">
    Move from staging to live payouts
  </Card>
</CardGroup>
