# Order lifecycle

> The five steps of an order, its eight states, and when to stop polling.

## The five steps

| Step | Call | Who |
|---|---|---|
| 1. Quote | `GET /v2/quote` prices the trade and returns the build request, signed and ready. | Your backend |
| 2. Build | `POST /v2/build` creates the order and returns the transactions to send. | Your backend |
| 3. Deposit | Send the approvals, then the deposit, on the source chain. | The user's wallet |
| 4. Track | Poll `GET /v2/orders/{orderId}` every 10 to 15 seconds until delivery succeeds or fails. | Your backend |
| 5. Cancel (optional) | `POST /v2/orders/{orderId}/cancel` settles a published order early. | The user signs; your backend relays |

`GET /v2/params` is optional. The quote is not: it is the only place the price comes from.

## States

`state` is a closed set of eight values. Client logic should depend on these strings only. Orders move forward and can skip states: `deposited` is skipped on a TRON source, and `partially_filled` when the first fill completes the order. `terminal_chain` usually lasts seconds, so a poller often sees `withdrawn_on_hub` right after `published`.

```mermaid
stateDiagram-v2
    [*] --> pending_deposit
    pending_deposit --> deposited
    pending_deposit --> published
    deposited --> published
    published --> partially_filled
    published --> terminal_chain
    partially_filled --> terminal_chain
    terminal_chain --> withdrawn_on_hub
    withdrawn_on_hub --> delivered
    withdrawn_on_hub --> delivery_failed
    delivery_failed --> delivered
```

| State | Meaning | Stop polling? |
|---|---|---|
| `pending_deposit` | Built. Waiting for the user's deposit. | No |
| `deposited` | The deposit was seen on the source chain and is on its way to the hub. Not entered on a non-EVM source. | No |
| `published` | The order is live as an intent and open to fills. `traces.rootIntentId` and `fullFee` are now set, and a cancel is possible from here on. | No |
| `partially_filled` | Part of the order filled and the rest is still open. Set on the first fill only. | No |
| `terminal_chain` | No more fills are expected: the order filled completely, reached its deadline, or was cancelled. Settlement follows automatically. | No |
| `withdrawn_on_hub` | Settled on the hub. Payout and refund delivery is not yet fully recorded. | No |
| `delivered` | Every payout and refund transfer was confirmed on its chain and recorded in `deliver`. | **Yes** |
| `delivery_failed` | At least one transfer could not be delivered after retries. The order is settled on the hub; `deliveryFailed` says which transfer. If the failed transfer is recovered, the order moves on to `delivered`. | **Yes** |

The terminal delivery states for client polling are **`delivered` and `delivery_failed`**. Keep polling through `withdrawn_on_hub`: settlement alone does not confirm receipt of the payout or refund.

Use a local polling timeout and retain the `orderId` so tracking can resume. A timeout means the result is unconfirmed; it does not cancel the order or imply `delivery_failed`. There is no expired state, so an order that is never deposited stays at `pending_deposit`.

Delivery tracking is best effort: an order can stay at `withdrawn_on_hub` after funds arrive if their delivery is never observed. A settlement with no payout or refund to transfer also stays at `withdrawn_on_hub`. Neither case is proof of a failed transfer; inspect `deliver` and the settlement amounts when reconciling a timed-out order. The API keeps checking for delivery for 12 hours after settlement. After that the order does not change on its own, so check the recipient on-chain and contact Khalani if a transfer is missing.

:::note[When to contact Khalani]
An order still at `deposited` a minute after its deadline will not publish or refund on its own, and `delivery_failed` needs a person. In both cases contact Khalani with the `orderId` and the `x-request-id` of your last read.
:::

## The deadline

Each order has a deadline, in Unix seconds: the time of the build plus your `orderTtl` (see `GET /v2/params`). It is signed into the order as the published intent's `validBefore`, and each order keeps the `orderTtl` it was built with.

- **The clock starts at the build, not the deposit.** The deposit still has to confirm on the source chain and reach the hub before the order publishes. Ask the user to deposit promptly.
- Only a TRON-source build response includes `deadline`. For an EVM source, record the time you called `/v2/build` and add your `orderTtl`, because no response carries the build time.
- A deposit that reaches the hub after the deadline does not publish and is not refunded on its own. The order stays at `deposited`, or at `pending_deposit` if the deposit landed more than a minute after the deadline. If the user deposited late, contact Khalani with the `orderId` and the deposit transaction hash.

## Settlement

An order settles in one hub transaction, triggered by whichever comes first:

- it fills completely,
- its deadline passes, or
- a cancel is relayed.

Settlement is usually submitted within a minute of the trigger; a relayed cancel submits it immediately. It pays the filled part to `payoutAddr` on the destination chain at the order's rate, and refunds the unfilled share of the deposit, after the flat fee, to `refundAddr` on the source chain. An order that never fills gets its deposit back, less the flat fee.

Orders can fill in parts, from more than one fill. `traces.chainObservations` gets one entry per fill, plus one for a cancellation or expiry.
