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

# Order lifecycle

> The statuses an order moves through, and the webhook event for each.

An order moves through a small, stable set of **public statuses**. Every read of an order —
`GET /v1/orders/{id}`, `GET /v1/orders`, and the `POST /v1/orders` response — reports one of these
five words. They are deliberately narrower than our internal fulfillment states, so your integration
never depends on an internal detail that could change.

## The five statuses

| Status       | Meaning                                             | Terminal |
| ------------ | --------------------------------------------------- | -------- |
| `pending`    | The order is accepted; payment is not yet settled.  | No       |
| `processing` | The order is paid and being fulfilled.              | No       |
| `completed`  | The order is fulfilled and its codes are available. | **Yes**  |
| `failed`     | The order could not be fulfilled.                   | **Yes**  |
| `refunded`   | The order was refunded.                             | **Yes**  |

A **terminal** status never changes again — stop polling once you see one. The only exception is that
a `completed` order can later become `refunded` if it is refunded by support.

## How an order progresses

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending
    pending --> processing
    pending --> failed
    processing --> completed
    processing --> refunded
    completed --> refunded
```

<Note>
  A sandbox order (placed with a test key) is fulfilled synchronously: it is born `completed` with
  deterministic fake codes, so you can exercise the full read-and-reveal flow without waiting.
</Note>

## Example

A completed order read with a test key, copied from a real call:

```json theme={null}
{
  "order": {
    "id": "572e24c6-270e-46a0-9b85-edd7eeb5290e",
    "status": "completed",
    "total": { "amount": "700", "currency": "XOF" },
    "createdAt": "2026-08-04T16:25:37Z",
    "lines": [
      {
        "productId": "f5baa307-b104-4a08-a6d8-a2b51b4b3292",
        "name": "100 Diamonds",
        "unitPrice": { "amount": "700", "currency": "XOF" },
        "quantity": 1,
        "lineTotal": { "amount": "700", "currency": "XOF" },
        "inputFields": { "player_id": "123456", "server": "asia-1" }
      }
    ]
  }
}
```

## Webhook events

Subscribe a [webhook](/webhooks) and we deliver a signed event when an order reaches a notable state:

| Order reaches                                 | Webhook event                       |
| --------------------------------------------- | ----------------------------------- |
| `completed`                                   | `order.fulfilled`                   |
| being handled by support (still `processing`) | `order.needs_attention`             |
| `refunded`                                    | `order.refunded`                    |
| `failed`                                      | **none** — poll to detect a failure |

<Warning>
  **A `failed` order emits no webhook**, and neither do `pending` or `processing`. A subscriber that
  never polls will wait forever for an order that failed: to detect a failure, **poll the order** until
  it reaches a terminal status. More generally, webhook delivery is at-least-once and best-effort — treat
  a webhook as a fast signal, never the sole source of truth, and reconcile by polling.
</Warning>

<Note>
  The success event is named `order.fulfilled`, while the status you read is `completed`: the two name
  the same outcome from two vocabularies (events and statuses). There is no `order.completed` event and
  no `fulfilled` status.
</Note>

## Internal states, for reference

Our fulfillment engine uses a longer set of states internally. You never receive these, but the table
maps them to the public status you do receive, so a value seen in a support conversation is
unambiguous:

| Internal state                          | Public status |
| --------------------------------------- | ------------- |
| `pending_payment`                       | `pending`     |
| `paid`, `fulfilling`, `needs_attention` | `processing`  |
| `delivered`                             | `completed`   |
| `failed`                                | `failed`      |
| `refunded`                              | `refunded`    |
