---
title: "Errors"
description: "Every B2B API error has the same JSON shape. Check error.code in your code, show error.message to people, and use error.details to fix fields."
---

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

# Errors

This page lists every error the API can return, and what to do about each one.

A `4xx` HTTP status means a problem on your side, such as a bad API key, a missing field, or a low store wallet balance. A `5xx` HTTP status means a problem on Cardzone's side.

## The error shape

Every error response has the same shape:

```json
{
  "error": {
    "code": "validation_failed",
    "message": "Validation failed",
    "details": {
      "product_id": ["is required"],
      "quantity": ["must be greater than 0"]
    }
  }
}
```

- `code` is a fixed, machine-readable value. Your code MUST make decisions based on it, together with the HTTP status.
- `message` is text for people. It may be translated if you send `Accept-Language`.
- `product_status` appears on product `404` errors when Cardzone knows why the product can't be ordered: `out_of_stock` or `unavailable`.
- `details` is optional. For `validation_failed`, it usually maps each field to its problems. But it can also be a single string, such as `"product_not_found"`. Check its type before you read it.
- Every response has an `X-Request-Id` header. Include it when you contact support about a request.

## Codes

| Status | `code` | Meaning | Retry? |
| --- | --- | --- | --- |
| 401 | `unauthorized` | The API key is missing, unknown, or revoked. Or it is a live API key whose store is inactive or not verified yet. See [authentication](https://docs.cardzone.ai/concepts/authentication/#what-a-failed-authentication-looks-like). | No |
| 403 | `forbidden` | The API key doesn't have the permission for this endpoint. | No |
| 403 | `insufficient_store_wallet_balance` | The store of a live API key is below the store wallet minimum. `details.minimum_prepaid_balance` has the amount. | After you recharge |
| 403 | `player_lookup_requirements_not_met` | The same store wallet rule, for player lookups. | After you recharge |
| 403 | `sandbox_order_cancellation_not_supported` | You called cancel with a sandbox API key. | No |
| 404 | `category_not_found` | [List products](https://docs.cardzone.ai/reference/products/list/): no category matches the `category_id`. | No |
| 404 | `validation_failed` with `details: "product_not_found"` | [Create order](https://docs.cardzone.ai/reference/orders/create/): the product doesn't exist, or this store can't order it now. When Cardzone knows why, `error.product_status` is `out_of_stock` or `unavailable`. See [availability](https://docs.cardzone.ai/concepts/catalog-and-recipients/#availability). | Later, for `out_of_stock`. Otherwise, refresh the catalog. |
| 404 | `product_not_found` | [Look up player profile](https://docs.cardzone.ai/reference/player-profiles/create/): the product doesn't exist, or it can't be fulfilled for this store. `error.product_status` is included when known. Lookups still work for products that are only out of stock or hidden. | Refresh the catalog |
| 404 | `validation_failed` with `details: "payment_option_not_found"` | [Create order](https://docs.cardzone.ai/reference/orders/create/): the product has no price set up that can be bought. This is rare. It is a catalog problem on Cardzone's side. | Contact support |
| 404 | `order_not_found` | No order with that `order_id`, or no API order with that `merchant_order_ref`, in this store. | No |
| 409 | `merchant_order_ref_already_used` | An order with this `merchant_order_ref` already exists. | No. Look it up by `merchant_order_ref`. |
| 409 | `order_not_cancelable` | See `details.reason_code`. | Maybe later |
| 422 | `validation_failed` | See `details` for each field. | Fix the request |
| 422 | `delivery_method_not_allowed` | Player lookup: this API key can't order this product's delivery type. [Create order](https://docs.cardzone.ai/reference/orders/create/) reports the same case as `validation_failed` with `details.delivery_type`. | No |
| 429 | `rate_limited` | Too many player lookups for this store. There is no `Retry-After` header. Wait, then retry, and wait longer each time (back off). | After a pause |

Here is a request that fails validation. Look at `details`:

> **Create order reports a missing product under validation\_failed**
>
> For historical reasons, [Create order](https://docs.cardzone.ai/reference/orders/create/) returns status `404` with `code: "validation_failed"` and `details: "product_not_found"` when the product is unknown or unavailable. To tell it apart from a `422` field error, check the HTTP status or `details`.

## Validation messages you'll see

| `details` entry | How to fix it |
| --- | --- |
| `"product_id": ["is required"]` | Send `product_id`. |
| `"quantity": ["must be greater than 0"]` | Send a positive whole number (integer). |
| `"merchant_order_ref": ["is too long (maximum is 128 characters)"]` | Shorten the `merchant_order_ref`. |
| `"recipient": ["is missing zone_id"]` | Send every key in the product's `recipient_fields`. |
| `"recipient": ["contains unexpected fields: email"]` | For a top-up, send only the keys in `recipient_fields`. |
| `"recipient": ["must be an object"]` | Send `recipient` as a JSON object. |
| `"product_id": ["is not a top-up product"]` | Player lookups only work for top-up products. |
| `"https_required"`, `"host_required"`, `"private_host"`, `"invalid_url"` (as the whole `details`) | Send a `webhook_url` that starts with `https://` and has a public host. |
| `"recipient": ["topup recipient fields are not accepted for voucher products"]` | Vouchers take only `name`, `email`, and `mobile`. |

Source: https://docs.cardzone.ai/resources/errors/index.mdx
