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

# Checkout status

> Poll safely and handle every checkout outcome.

## Purpose

Use `GET /v1/merchant-api/checkouts/{checkoutId}/status` with the merchant API
key to recover the current hosted checkout. Poll with backoff when webhooks
are not suitable; do not infer completion from the return URL.

| Status                    | Terminal                | Fulfillment allowed | Meaning                                                                       |
| ------------------------- | ----------------------- | ------------------- | ----------------------------------------------------------------------------- |
| `created`                 | No                      | No                  | Hosted session exists and awaits customer activity                            |
| `initiated`               | No                      | No                  | A deposit was created and dispatch started                                    |
| `pending`                 | No                      | No                  | Provider processing has started                                               |
| `active`                  | No                      | No                  | The provider trade is active                                                  |
| `hold`                    | No                      | No                  | The CS2 trade is in its provider hold period                                  |
| `completed`               | Yes                     | Yes                 | Provider-verified payment completion                                          |
| `canceled`                | Yes                     | No                  | Customer or provider canceled the attempt                                     |
| `declined`                | Yes                     | No                  | Provider declined the attempt                                                 |
| `failed`                  | Yes                     | No                  | Processing failed                                                             |
| `reverted`                | Yes                     | No                  | A previously progressing trade was reverted                                   |
| `expired`                 | Yes                     | No                  | The offer-acceptance deadline elapsed before an attached trade could continue |
| `reconciliation_required` | No automatic transition | No                  | Stop automation and contact support                                           |

The normal forward path is `created` → `initiated` → `pending` or `active` →
optional `hold` → `completed`. A checkout may instead move from a non-terminal
state to an unsuccessful terminal state. Provider callbacks are authoritative,
so integrations must tolerate skipped observable states and must not manufacture
transitions locally.

For checkout deposits, respect the returned `pollAfterSeconds`. Continue polling
a pending, active, or hold state. A status capability returned by a successful
deposit remains usable after the offer deadline so an attached protected trade
can finish.

For CS2, `reservationRequired` is true. Once hold begins, the CS2 trade is held
for eight days; keep the merchant order reserved and do not fulfill, credit,
delete, or release it until `payment.completed`. Use `holdUntil` from the API,
not a browser timer. `fulfillmentAllowed` is false until completion. On
canceled, declined, failed, or reverted outcomes it remains false and you may
release the reservation; reconciliation is manual.

```ts theme={null}
const result = await fetch(
  `${baseUrl}/v1/merchant-api/checkouts/${checkoutId}/status`,
  { headers: { Authorization: `Bearer ${apiKey}` } }
);
if (!result.ok) throw new Error(`status request failed: ${result.status}`);
const checkout = await result.json();

if (checkout.status === "completed" && checkout.fulfillmentAllowed === true) {
  // This transaction only records completion and enqueues a unique durable job.
  // It never calls an external fulfillment service.
  await recordCompletionAndEnqueue({
    checkoutId: checkout.id,
    merchantOrderId: checkout.merchantOrderId
  });
}
```

**Expected result:** one order is fulfilled at most once, only after
`completed`. Polling and webhook handlers must call the same enqueue operation.
Implement the worker and crash recovery in
[Safe fulfillment](/fulfillment-safety). Use
[webhooks](/webhooks/overview) for durable notifications.
