# Order statuses

> Every status an API order can have, what each one means, what to show your user, and which statuses are final.

- Source: https://docs.kotova.io/x/api/order-statuses
- API base URL: https://api.kotova.io/x/v1
- OpenAPI document: https://api.kotova.io/x/v1/openapi.json

An order has exactly one status at a time. The set of statuses is fixed within v1, so
you can map every one of them to what your user sees.

## Status table

`status` is one of these nine values. A new one would only come with a new API version,
never within v1. `is_final` says whether the order can still change.

| `status` | What it means | `is_final` |
| --- | --- | --- |
| `waiting_for_deposit` | The order exists at the source and waits for your user's deposit. | `false` |
| `confirming_deposit` | The source has seen the deposit and is waiting for its network confirmations. | `false` |
| `exchanging` | The deposit is confirmed and the source is performing the swap. | `false` |
| `sending` | The source is sending the payout. The order stays here until the payout's transaction is recorded, even when the source already reports the swap as done. | `false` |
| `completed` | The payout was sent: `transactions.payout` has its hash, and `completed_at` is set. | `true` |
| `action_required` | The source reported a problem with the order, or is holding it. `status_detail` says why. | `false` |
| `refunding` | A refund is under way. | `false` |
| `refunded` | The refund was sent. | `true` |
| `expired` | No deposit arrived before the deadline. | `false` at first, `true` once a late deposit can no longer revive the order |

The status is worked out from the order's stored state each time you read it, and a
read may first refresh that state from the source. Read the order with
[`GET /orders/{id}`](https://docs.kotova.io/x/api/orders#reading-an-order) until `is_final` is `true`.

## Final statuses

- **`completed`** and **`refunded`** are final as soon as they are reached.
- **`expired`** becomes final only once the source's late-deposit window has passed.
  The window is counted from the order's creation: usually 24 hours, and shorter at some
  sources for some rate types. Until then a deposit that arrives late can still revive
  the order, and it moves on to `confirming_deposit`, or to `action_required` with the
  reason `late_deposit`. Whether a late deposit is honoured is the source's decision.
- At most sources, an order still waiting when its `expires_at` passes shows as
  `expired` straight away. Where the source can extend its deadline, the order stays
  `waiting_for_deposit` until the source says otherwise.

Decide on `is_final`, never on the status name: an `expired` order whose `is_final` is
`false` can still change.

## How an order moves

A typical order goes through these statuses in this order:

1. `waiting_for_deposit`
2. `confirming_deposit`
3. `exchanging`
4. `sending`
5. `completed`

Other paths:

- **No deposit:** `waiting_for_deposit` → `expired`. A late deposit inside the window:
  `expired` → `confirming_deposit` and on along the typical path, or `expired` →
  `action_required` (`late_deposit`).
- **A problem with the deposit or the swap:** usually `confirming_deposit` or
  `exchanging` → `action_required`. Then, by your user's choice, the automatic resolution or the
  source's own decision: → `refunding` → `refunded`, or back to the typical path
  (`exchanging` → `sending` → `completed`).
- **A refund the source starts itself:** → `refunding` → `refunded`.
- **A rejected refund address:** `action_required` (`refund_address_rejected`) until your
  user gives a new one, then usually `refunding` → `refunded`.
- **A payout on hold:** `sending` → `action_required` (`payout_on_hold`) → `completed`
  once the source sends the payout.

You will not necessarily see every step. A read shows the status at that moment, and a
fast order can move several steps between two reads. Statuses do not always move
forward through the table either: an `expired` order can come back, and a `sending`
order can become `action_required`.

## What to show your user

- **`waiting_for_deposit`:** `deposit.address`, `deposit.amount` and `from.network`, and
  `deposit.memo` under the name in `deposit.memo_label` when it is set. Your user must
  send the memo with the deposit, so show it prominently. Show the time left until
  `expires_at`, and ask your user to send exactly `deposit.amount` on `from.network`.
- **`confirming_deposit`:** the deposit has arrived and is being confirmed. Show
  `transactions.deposit.confirmations` out of `required_confirmations` when both are
  known, and link to `transactions.deposit.explorer_url`.
- **`exchanging`:** the source is performing the swap. `estimated_duration_seconds`
  gives a rough idea of the time left.
- **`sending`:** the payout is on its way to `to.address`. Link to
  `transactions.payout.explorer_url` once the payout transaction is there.
- **`completed`:** the amount sent, `to.amount_sent`, and a link to the payout
  transaction.
- **`action_required`:** what `status_detail` says, as described in
  [Status detail](https://docs.kotova.io/x/api/order-statuses#status-detail).
- **`refunding`:** a refund to `refund.address` is under way. `status_detail.reason`
  says why, when it is known.
- **`refunded`:** the refund was sent. Link to `transactions.refund.explorer_url`.
- **`expired`:** no deposit arrived in time. If your user sent nothing, nothing is owed:
  offer a new quote. If they did send funds, keep following the order while `is_final`
  is `false`, and write to api@kotova.io with the order id if it does not move.

Telling your user about each change is up to you. The order page of an API order has
no e-mail updates (see [The order page](https://docs.kotova.io/x/api/orders#the-order-page)).

## Status detail

`status_detail` explains an order that needs attention. It is set for every
`action_required` order, and for a `refunding` order when the reason is known. Otherwise
it is `null`.

| Field | What it is |
| --- | --- |
| `reason` | Why the order needs attention. See the table below. |
| `since` | When the source first reported the problem, or for `payout_on_hold` when the hold was detected. `null` when unknown. |
| `auto_resolution` | What happens at `auto_resolution_at` if nobody acts first: `refund` (the deposit goes back to `refund.address`) or `continue` (the source carries on with the swap at its current rate). `null` when nothing happens on its own: the source resolves it itself, a choice has already been made, or someone has to act. |
| `auto_resolution_at` | When the automatic resolution happens: about an hour after the problem was first reported, or after the source last refused a refund address. `null` without an automatic resolution, or when unknown. |
| `resolve_url` | The order page, where your user can choose between the options the source offers, or give a new refund address. `null` when there is nothing to choose. |

On a `refunding` order, only `reason` and `since` are set; the other three fields are
`null`.

`reason` is an extensible list: treat a value you do not know as `other`.

| `reason` | What happened | What to do |
| --- | --- | --- |
| `amount_mismatch` | The deposit differs from the order's amount, or is outside the source's limits. | When `resolve_url` is set, send your user there to choose. Show what `auto_resolution` will do, and when. |
| `late_deposit` | The deposit arrived after the order had expired. | The same. |
| `source_review` | The source holds the order for a review, and resolves it itself. | Nothing to choose. Tell your user the source is reviewing the order, and keep following it. |
| `payout_on_hold` | The source reports the swap as done, but has not sent the payout. | Write to api@kotova.io with the order id. See [Payout on hold](https://docs.kotova.io/x/api/order-statuses#payout-on-hold). |
| `refund_address_rejected` | The source refused the refund address, and it was removed. | Your user gives a new refund address at `resolve_url`. |
| `other` | Any other problem the source reported. | When `resolve_url` is set, send your user there. Otherwise write to api@kotova.io with the order id. |

When you send your user to `resolve_url`, tell them that the page opens with the address
the order pays out to (`to.address`), and show what happens if they do nothing:

- `auto_resolution: refund`: the deposit is refunded to `refund.address` at
  `auto_resolution_at`.
- `auto_resolution: continue`: the swap continues at the source's current rate at
  `auto_resolution_at`.
- `auto_resolution: null`: nothing happens on its own, so your user's choice matters.

Endpoints for these choices, and for giving a new refund address, are not available yet
in the API: your user makes the choice on the order page. How each source handles these
cases is described in [Emergency states](https://docs.kotova.io/x/how-it-works/order-lifecycle#emergency-states)
and [Refunds, cancellations and emergencies](https://docs.kotova.io/x/how-it-works/refunds).

### Payout on hold

At some sources, an order can be reported as done while the source still holds the
payout. When no payout transaction has been recorded ten minutes after the source
reported the swap as done, the order changes from `sending` to `action_required`, with
this `status_detail`:

```json
{
  "reason": "payout_on_hold",
  "since": "2026-11-10T10:38:20Z",
  "auto_resolution": null,
  "auto_resolution_at": null,
  "resolve_url": null
}
```

> [!WARNING]
> **Contact support with the order id**
>
> There is nothing for your user to choose, and nothing happens on its own. Write to
> api@kotova.io with the order `id`, and tell your user that the payout is being checked
> with the source.

Kotova keeps checking with the source for a while, and the order becomes `completed` on
a later read once the payout is recorded. If it stays on hold, support follows it up.

## How these map to the app

The order page at `order_url` uses the app's own labels. They correspond like this:

| API `status` | Order page |
| --- | --- |
| `waiting_for_deposit` | Initiated |
| `confirming_deposit` | Received |
| `exchanging` | Confirmed |
| `sending` | Sending |
| `completed` | Complete |
| `action_required` | Emergency ("Action required"), or On hold ("Payout on hold") for `payout_on_hold` |
| `refunding` | Refunding |
| `refunded` | Reverted |
| `expired` | Expired |

See [Status vocabulary](https://docs.kotova.io/x/how-it-works/order-lifecycle#status-vocabulary) for what
your user reads there.
