---
title: "Retry safely"
description: "Send a merchant_order_ref so a retried order request never buys twice, and find an order when you never got the create response."
---

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

# Retry safely

This page shows how to retry order requests without buying the same item twice. Read it before you write your order code.

Here is the problem. Sometimes your `POST /orders` request reaches Cardzone, and Cardzone accepts the order and charges for it. But a network error stops the response from reaching you. If you then send the request again with no plan, you buy the item twice.

A request is **safe to retry** (idempotent) when sending it twice has the same result as sending it once. `merchant_order_ref` makes order creation safe to retry.

## Send a `merchant_order_ref` with every order

`merchant_order_ref` is your own ID for the order. The API accepts orders without it, but then you can't retry safely. So always send one.

Rules for `merchant_order_ref`:

- It can be up to 128 characters long.
- It must be unique across every order in your store.
- Use the order ID or line ID from your own system.
- Add a prefix that only your API integration uses, such as `api-`.

Why the prefix? Cardzone's other order channels also store `merchant_order_ref` values. If one of them uses the same value, your API order is blocked (see the `409` step below).

```json
{
  "product_id": "018f7b75-0194-726a-9814-e26efa39dfa1",
  "quantity": 1,
  "merchant_order_ref": "shop-order-1001",
  "recipient": { "player_id": "00123456789" }
}
```

If you send the same `merchant_order_ref` again, Cardzone never creates a second order. Instead, it returns this body:

```json
{
  "error": {
    "code": "merchant_order_ref_already_used",
    "message": "merchant_order_ref has already been used for this store"
  }
}
```

The HTTP status is `409 Conflict`. Treat it as "this order already exists". It is not a failure.

Cardzone checks only the `merchant_order_ref`. It doesn't compare the rest of the body. So if you reuse a `merchant_order_ref` for a different product, you also get `409`.

## When the create response is lost

Follow these steps when the create response is lost (a timeout, a connection error, or a `5xx`), or when you get a `409`:

1. **Retry with the same merchant\_order\_ref, never a new one**

   A new `merchant_order_ref` creates a new order. Create the `merchant_order_ref` once, when your customer checks out. Reuse it on every attempt. The only exception is in the last step below.
2. **On a timeout or 5xx, look up the order by your merchant\_order\_ref**

   `GET /api/b2b/v1/orders/shop-order-1001` accepts your `merchant_order_ref` in place of the `order_id`. A `200` means the order exists. Save its `order_id` and continue.
3. **On 404, create the order again with the same merchant\_order\_ref**

   Cardzone never created the first order. It is safe to retry, because the `merchant_order_ref` still protects you from duplicates.
4. **On 409, look it up too**

   A `409 merchant_order_ref_already_used` means an order with that `merchant_order_ref` exists. Look it up by `merchant_order_ref`, the same way as after a timeout. If that lookup returns `404`, the `merchant_order_ref` belongs to an order from another channel, not an API order. In that case, create the order again with a new `merchant_order_ref`.

## Which errors to retry

A `4xx` status means a problem on your side. A `5xx` status means an error on Cardzone's side.

| Response | Retry? |
| --- | --- |
| Timeout, connection error, `5xx` | Yes. Use the steps above. Wait longer before each new try (exponential backoff). |
| `429 rate_limited` | Yes, after a pause. |
| `409 merchant_order_ref_already_used` | No. Look up the order by `merchant_order_ref`. |
| `401`, `403` | No. First fix the API key, its permissions, or the store wallet balance. |
| `404` with `details: "product_not_found"`, `422 validation_failed` | No. Fix the request, or refresh your catalog. |

> **The sandbox doesn't save orders**
>
> The sandbox returns your `merchant_order_ref` in the response, but it doesn't save it. So it never returns `409`, and it can't look up orders by `merchant_order_ref`. To test this flow, use a live API key with a cheap product. Or mock (fake) those two responses in your tests.

Source: https://docs.cardzone.ai/guides/idempotency-and-retries/index.mdx
