> For the complete documentation index, see [llms.txt](https://docs.native.org/native-dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.native.org/native-dev/build-with-native/native-core/guides/handle-timeouts.md).

# Handle outcomes & timeouts

What to do with each /trade outcome — when to resend, when to reconcile, and how to never double-fill.

`POST /trade` is synchronous and returns the same JSON shape whatever the HTTP status, so **branch on `submission_status` in the body, not the status line.** (The one exception is a body the service rejects before it reaches the handler — over the 256 KiB limit — which comes back as plain text with no `submission_status` at all. Guard your parse.) This guide is the decision playbook; the full code catalog is in [Error responses](/native-dev/build-with-native/native-core/reference/error-responses.md).

## Read two fields, not one

`submission_status` tells you whether the **transaction** landed. It does **not** tell you whether the **order** succeeded — that lives in the `response` envelope, present on every `accepted` reply.

| `submission_status` | What happened                                                                                                                                     | What to do                                                                                                           |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `accepted`          | The transaction landed and reached execution.                                                                                                     | **Not done — read `response`.** It carries `{"open":…}`, `{"filled":…}`, `{"cancelled":…}`, or `{"error":"<code>"}`. |
| `rejected`          | Refused before execution (shaping / rate limit / suspension / expiry / admission), or an envelope-level execution failure. `error.code` says why. | Fix the cause, submit a **fresh** action. One exception below.                                                       |
| `timeout`           | The outcome was not observed in the 3-second wait budget, or the submission could not be routed.                                                  | Depends on the code — see [below](#reconciling-a-timeout).                                                           |

{% hint style="warning" %}
**`accepted` is not success.** An order that failed at execution — insufficient balance, below minimum notional, off the tick grid — still returns `accepted` with **no** top-level `error`; the code appears only inside `response`, at `response.status.error` for a single order. A client that branches on `submission_status` alone records a rejected order as live and will keep quoting against a position it never had.
{% endhint %}

```json
{
  "submission_status": "accepted",
  "tx_hash": "0x...",
  "response": { "type": "order", "status": { "error": "insufficientspotbalance" } }
}
```

The full leaf vocabulary is in [POST /trade](/native-dev/build-with-native/native-core/reference/post-trade.md#what-accepted-carries). You only need [`orderStatus`](/native-dev/build-with-native/native-core/reference/post-info.md#orderstatus) afterwards to reconcile a `timeout`, or to re-read an order later in its life.

## Resending the same signed action

Three rejections are pure backpressure: nothing about your action was wrong, so back off and send the **same signed action** again.

| Code                   | HTTP | Back off by                         |
| ---------------------- | ---- | ----------------------------------- |
| `RateLimited`          | 429  | `error.retry_after_ms`              |
| `TooManyPending`       | 503  | `error.retry_after_ms` (50 ms)      |
| `QueryLagBackpressure` | 503  | a moment, until the node catches up |

Every other `rejected` needs a fresh action after you fix the cause — never blindly resend.

## Reconciling a timeout

Not every `timeout` is indeterminate. The `error.code` tells you whether the transaction ever reached a node, and the two cases need opposite handling.

| Code                               | HTTP | Reached a node?                                   | What to do                                                     |
| ---------------------------------- | ---- | ------------------------------------------------- | -------------------------------------------------------------- |
| *(none)* — the wait budget elapsed | 200  | **Yes**, it is executing                          | Reconcile by `cloid`. **Never** resubmit under a new nonce.    |
| `Unavailable`                      | 503  | **No** — refused before the write left the API    | Resubmit the same signed bytes. There is nothing to reconcile. |
| `NodeUnreachable`                  | 504  | **Unknown** — the connection broke mid-submission | Reconcile by `cloid`. **Never** resubmit under a new nonce.    |

Treating the whole 503 family as indeterminate silently drops every write for the duration of a leadership handoff, which is why it is worth separating. Treating the 200 and 504 cases as safe to resubmit is how you double-fill.

{% hint style="info" %}
`Unavailable` is returned only when the write was refused before it reached a node, so a resubmit cannot duplicate it. A failure that happens *after* the transaction went out returns `NodeUnreachable` instead, on a separate path — that one is the uncertain case, and the only one of the two you reconcile.
{% endhint %}

**Set your HTTP client timeout above 10 seconds.** The 3 seconds above is only part of it — a slow call can take up to 10. Giving up earlier turns a reply that was about to arrive into the uncertain case this page exists to resolve.

When a code is not in this table, reconcile.

To reconcile, look the action up by the `cloid` you sent:

* [`orderStatus`](/native-dev/build-with-native/native-core/reference/post-info.md#orderstatus) — an order's current lifecycle by `cloid`.
* [`txStatusByCloid`](/native-dev/build-with-native/native-core/reference/post-info.md#txstatusbycloid) — a non-order action (`withdraw` / `settle` / `repay`) by `cloid`, within the recent window.

This is why every order should carry a `cloid` — it is your only handle for reconciliation.

## Batches

A [`batch`](/native-dev/build-with-native/native-core/reference/post-trade.md#batch) is one envelope with **one** `submission_status`, but its `response.statuses[]` carries one sub-response per item, in item order — so an accepted batch already tells you which items rested, filled, or failed. Read each item's outcome at `statuses[i].status`, one level below where a `cancelAll` leaf sits. Reconcile per item by `cloid` via [`orderStatus`](/native-dev/build-with-native/native-core/reference/post-info.md#orderstatus) only when the envelope came back `timeout`.

## Next steps

* [POST /trade](/native-dev/build-with-native/native-core/reference/post-trade.md#what-accepted-carries) — the full `response` envelope
* [Error responses](/native-dev/build-with-native/native-core/reference/error-responses.md) — every code and what it means
* [Trade over REST](/native-dev/build-with-native/native-core/guides/trade-over-rest.md) — the happy path
