Skip to main content
The single most common bug written against Anthid is a dashboard or a strategy that treats SENT as done. It is an easy mistake, because on most broker APIs a status is a status. On Anthid an intent reports two different answers about where things stand, and they are not interchangeable. This article explains the two, why there are two, and which one to read for each question you actually have.

Two questions, two fields

An instruction goes through two independent things. Anthid has to carry it to the broker, and then the broker has to do something with it. Each is reported on the intent. Dispatch is how far Anthid got in handing the instruction over. It is the state field, and it moves through four values. Dispatch is terminal at SENT or FAILED. Once an intent is SENT, it stays SENT forever, including after the order fills, is cancelled, or is rejected at the venue. That is the point at which people go wrong: SENT is the last thing dispatch will ever say, so it looks final, and it is final, but only about the handoff. Outcome is what the broker did with the order that followed. It is the runtime_status field, and it keeps moving after dispatch has stopped. A perfectly healthy order reads state: SENT, runtime_status: OPEN for as long as it rests, then state: SENT, runtime_status: FILLED. A rejected one reads state: SENT, runtime_status: REJECTED. In both cases dispatch succeeded. The handoff and the outcome are different facts.

Why one field cannot do this job

Collapse the two into one status and something has to give. If the single status is dispatch-shaped, it ends at SENT and can never say filled. If it is outcome-shaped, it cannot begin until the broker speaks, so an order the broker never acknowledged has no status at all, which is exactly the order you most need to point at. And if it tries to be both, PENDING becomes ambiguous: is the platform still holding the instruction, or is the broker? That last ambiguity is why runtime_status is null rather than PENDING before the broker reports. A null says Anthid has heard nothing. PENDING is the broker saying it has the order and has not worked it. A caller that only wants one value can treat null as pending. A caller watching for a stuck order needs to tell them apart, because an intent that has been SENT for a minute with a null outcome is a different problem from one the broker is holding.
SENT does not mean filled. A report that counts SENT intents as executed overstates what traded, by every order that was rejected, cancelled, or is still resting. Read runtime_status, or read filled_quantity from the resulting order.

The order carries the same idea twice more

Once the broker acknowledges an order there is an order record alongside the intent, and it makes the same distinction in two more places. The order carries order_status, the precise lifecycle position in the venue’s own vocabulary, and order_state, a coarse rollup computed from it for filtering lists and driving dashboards. Because the state is derived from the status, the two cannot disagree. Two consequences bite in reporting, and both are the SENT mistake in a different coat. A partially filled order is Open, not Closed. It has traded, but it is still working, and closing it out in your own model on the first fill strands the remainder. Closed is not a synonym for filled. A cancelled order and a fully filled order are both Closed, because both are finished. Which of the two it was comes from order_status, or from comparing filled_quantity against quantity.
The two fields do not share a casing. order_state serializes as Open, Closed, Rejected. order_status beside it serializes as NEW, FILLED, PARTIALLY_FILLED. A comparison against "OPEN" never matches, and it fails silently.

Which field answers which question

filled_quantity is the only field that reports how much executed, and it is meaningful at every state: partial on an open order, complete on a filled one, and zero on a cancelled one that never traded. Do not infer execution from any state or status field.

Reading it from the stream

The same facts arrive live over gRPC. A BrokerOrder event on the streaming API carries the order’s status and its cumulative filled_quantity, and a BrokerTrade event carries each individual execution for the venues that report them. If you want to know how much of an order has traded, read filled_quantity on BrokerOrder; it already aggregates the trades. Reconnecting does not replay every missed transition. Orders default to live delivery, but ORDER_DELIVERY_PRIMED first sends the latest retained snapshot per order and a completion marker. See Streaming. After a disconnect, read GET /v1/orders?environment=<env> for the latest known state per order rather than assuming continuity. The stream is a view over the record, not a replacement for it.

A worked example

An intent is submitted at 09:30:00 and returns an intent_id. Reading it back:
Anthid has recorded it and not yet picked it up. A moment later:
The broker accepted the handoff and has not reported. This is normal for a few hundred milliseconds. If it lasts a minute, something is wrong on the broker side, and this is the shape a stuck-order monitor should look for. Then:
Working at the venue. Then:
Some traded. The order is Open, and filled_quantity on the order record says how much. Finally:
Dispatch never changed after the second reading. Everything you cared about happened in the other field.

Intents

The concept page: dispatch, outcome, and the action history.

Intents API

Field names, endpoints, and the state versus runtime_status contract.