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

# Errors

> Status codes, error codes, and what to do about each.

Errors return a JSON body with a stable `error_code` you can branch on. Match on the code, not the message — messages may be reworded.

```json theme={null}
{
  "error": "Amount is below the minimum for this chain",
  "error_code": "amount_below_minimum"
}
```

## Status codes

| Status | Meaning                           | Retry?                       |
| ------ | --------------------------------- | ---------------------------- |
| `400`  | Malformed request                 | No — fix the request         |
| `403`  | Not permitted for your account    | No                           |
| `404`  | Not found                         | No                           |
| `409`  | Conflict, e.g. quote already used | No                           |
| `422`  | Valid shape, unprocessable        | Depends                      |
| `429`  | Rate limited                      | **Yes**, after `Retry-After` |
| `500`  | Our fault                         | Yes, with backoff            |
| `502`  | Upstream chain data unavailable   | Yes, with backoff            |

## Error codes

<AccordionGroup>
  <Accordion title="amount_below_minimum" icon="ruler">
    The order is too small for the deposit rail on that chain, where we sponsor a fixed cost per payment.

    **Do:** offer the connect rail, which has no minimum, or suggest a cheaper chain. [Limits](/reference/limits).
  </Accordion>

  <Accordion title="merchant_not_attested" icon="bullseye">
    No payout address is attested on-chain for this merchant, so a payment would have nowhere safe to go.

    **Do:** set your payout address in **Settings → Payout**. If a customer hit this, the checkout will have shown "merchant hasn't completed setup" rather than taking money.
  </Accordion>

  <Accordion title="pool_not_deployed" icon="cube">
    The connect rail needs a deployed pool on that chain. `deposit()` is a contract call and reverts against an address with no code.

    **Do:** deploy the pool, or use the deposit rail, which works pre-deploy.
  </Accordion>

  <Accordion title="chain_not_activated" icon="link">
    That chain isn't enabled on your account.

    **Do:** enable it in **Tokens**, or quote a chain you have enabled.
  </Accordion>

  <Accordion title="token_not_enabled" icon="coins">
    That (token, chain) pair is disabled. Enforced server-side, so hiding it in your UI isn't sufficient on its own.

    **Do:** enable the pair, or quote a different one.
  </Accordion>

  <Accordion title="quote_expired" icon="clock">
    The price window lapsed before payment creation.

    **Do:** create a fresh quote. Note this concerns *quoting* — a customer who pays a previously issued address late is still credited. [Finality](/concepts/finality).
  </Accordion>

  <Accordion title="payment_blocked" icon="ban">
    A compliance check refused the payment. Deliberately non-specific, to avoid coaching evasion.

    **Do:** nothing automated. Only fires if you've enabled payer screening. [Sanctions](/compliance/sanctions).
  </Accordion>

  <Accordion title="rate_limited" icon="gauge-high">
    Too many requests.

    **Do:** back off for the duration in `Retry-After`. The embedded checkout handles this automatically with a countdown.
  </Accordion>

  <Accordion title="pool_mismatch" icon="triangle-exclamation">
    Not an API error — a **checkout state**. The on-chain verification failed, so signing is blocked and the payment address is hidden.

    **Do:** treat as serious. Check your payout settings and the `settlementAnchor` in your embed. This is the protection doing its job. [Verification](/concepts/verification).
  </Accordion>
</AccordionGroup>

## Handling them

```javascript theme={null}
const res = await createQuote(cart);

if (!res.ok) {
  const { error_code } = await res.json();

  switch (error_code) {
    case "amount_below_minimum":
      return showConnectRailOnly();          // recoverable in-flow

    case "chain_not_activated":
    case "token_not_enabled":
      return showAlternativeChains();

    case "rate_limited":
      return retryAfter(res.headers.get("Retry-After"));

    case "merchant_not_attested":
    case "pool_not_deployed":
      alertOps(error_code);                  // your config, not the customer's problem
      return showGenericFailure();

    default:
      return showGenericFailure();
  }
}
```

<Tip>
  Distinguish **customer-recoverable** errors — try another chain, wait a moment — from **merchant configuration** errors, which the customer can do nothing about. The second kind should page you, not confuse them.
</Tip>

## Still stuck

<Columns cols={2}>
  <Card title="Check the dashboard" icon="table" href="/dashboard/overview">
    Readiness state, recent payments, webhook deliveries.
  </Card>

  <Card title="Verify on-chain" icon="magnifying-glass" href="https://www.cryptocheckout.ai/verify">
    Confirm contracts and addresses independently.
  </Card>
</Columns>
