Agara

Operations

The operational mechanics for running a quote loop on Agara — post-only, self-trade prevention, cancel-all, and how to wire the streaming surfaces into a robust loop.

This page covers the per-call mechanics that matter most when you're managing resting orders through the API: placing quotes, controlling exposure, and keeping your view of the market current.

API order placement

For API trading, use pre-signed orders. Server-side signing may slow order ingestion. Follow the one-time setup and grant orders:place_signed for signed single-order and batch submissions.

Quote loop shape

A minimal quote loop looks like this:

  1. Subscribe and buffer the orderbook stream and the account stream for your markets.
  2. Complete the full quote-readiness state machine while buffering. This includes the active-subscription + REST-watermark orderbook fence; the fully paginated order, positions, summary, trades, activities, and open-order REST seed; reconciliation of buffered account events; and the market-lifecycle risk decision. Do not place from a smaller subset of those checks.
  3. Place initial quotes using pre-signed, post-only limit orders.
  4. On every orderbook update, route on data.kind. Replace your entire ladder for snapshot; apply changed levels for delta. Current v1 sends full snapshots for book changes, so a loop that reacts only to deltas will miss live moves. Then decide whether to re-quote — cancel stale orders and place fresh ones at the new desired prices.
  5. On each fill, update inventory and reconsider sizing.
  6. On sequence_reset or stream disconnect, pause placements, buffer, and re-seed both public and account state before trusting local state again.

Use the complete quote-readiness state machine for timeouts, resets, reconnects, and process restarts.

Follow market status and new cycles

Subscribe to market_status by condition for every market you quote. Stop placing on market_halted, market_resolved, or payouts_resolved, reconcile open orders, and refresh market state before resuming after market_resumed. A proposal (outcome_proposed, or payouts_proposed for a split) is not a final resolution. A split resolution arrives only as these payouts_* kinds, never as a winner with an empty token, so route on kind.

For recurring events, also subscribe to market_status by event. current_market_changed supplies the newly active market and outcome IDs; market_resolution_completed supplies the resolved market, its winner or split payouts, and replacement details. Start the quote-readiness procedure for a new active market before quoting it. An upcoming or merely expected replacement is not ready to trade. Keep reconciling orders and positions for the previous cycle.

Event subscriptions send future changes without an initial snapshot or replay. Use the market discovery workflow to seed current state and find new events through REST.

Post-only

Set post_only: true on any limit order you don't want to take liquidity. If your price would cross the book on arrival, the order is rejected rather than filled as a taker. Most of the time this comes back immediately as a 422 on the POST response; if the book moves after that check, it instead surfaces asynchronously as a REJECTED order — an order_rejected event, or poll /trade/v1/orders/{id}.

Use this on every quote in your loop. Without it, a fast move in the book between your decision and your submission silently turns you from maker to taker and makes the fill subject to taker fees.

{
  "token_id": "21742…36455",
  "side": "BUY",
  "type": "LIMIT",
  "time_in_force": "GTC",
  "price_micro": "300000",
  "shares_micro": "100000000",
  "post_only": true
}

See Orders → post-only for the full semantics.

An order can take liquidity on the complementary outcome too. For example, BUY YES can match BUY NO. The requested outcome's orderbook already includes this complementary liquidity at its effective price. Use that combined depth when choosing a post-only price, and track your own quotes on both outcomes; see Cross-orderbook matching for examples.

Self-trade prevention

Orders from the same account cannot fill each other. The maker side is always cancelled when matching encounters your own eligible resting order. The incoming order continues matching, and an order_cancelled frame fires for the cancelled maker with reason: "SELF_TRADE_PREVENTION".

This is usually what you want — you'd rather cancel an old stale quote than match against yourself and waste fees on a no-op trade. But it does mean your re-quote loop needs to account for it:

  • If you cancel-then-place, the new place may STP-cancel an older order you didn't realize was still resting. Log the SELF_TRADE_PREVENTION cancels so you can audit later.
  • If a new order crosses your own older resting order, the older maker order is cancelled. The new order continues and may match another account or rest on the book.
  • Track your quotes on both outcomes: complementary BUY/BUY and SELL/SELL matches use the same rule. Post-only does not prevent these cancellations.

See Self-trade prevention for the mechanics.

Cancel-all

POST /trade/v1/orders/cancel-all schedules cancellation of every nonterminal order visible when each wallet's sweep begins. It returns 202; cancellation completes asynchronously. Use it as a kill switch:

  • Disconnects. If you cannot trust your local book, request cancellation, reconnect, then reconcile before resuming.
  • Process restart. Bot crashed, you don't trust your in-memory state — cancel everything from a fresh process, reconcile.
  • End of session. Stop quoting without leaving stale liquidity behind.

Don't wait for one order_cancelled frame per order. An order that is still PENDING is cancelled without one. An order that is already SUBMITTING is cancelled only if it rests on the book, so it can still fill first, or end REJECTED or FAILED; reconcile its fills and the status it reaches. A CANCELLED status is provisional: an order that filled just before cancel-all reached it can still change to MATCHED, so follow the late-fill guidance in Orders → cancel an order before you resume.

After the 202, treat its as_of as a conservative reconciliation upper bound and page POST /trade/v1/orders/list to the end. Confirm every order sent no later than that time has is_terminal: true, including completed FAK partial fills and orders that had not reached the book. Each wallet's sweep can begin before the response timestamp, so do not assume as_of is the exact cutoff. Do not rely on portfolio open orders alone or assume that the response made the book empty. Cancel a late order or repeat cancel-all, then repeat the full order check.

Scope: orders:cancel_all. See Orders → cancel everything open.

There's no cancel-by-market or cancel-by-token-id endpoint today. If you need to clear one market's orders, fully page POST /trade/v1/orders/list, filter the result by token_id, and cancel every nonterminal match by ID. The portfolio open-orders view also shows active orders, but the full order list lets the same recovery ledger follow each ID into a terminal state. This per-order approach is acceptable for low-N cases; if you're running many markets and need a faster path, let us know.

Cancel-on-disconnect

We don't auto-cancel resting orders on WebSocket disconnect today. A network blip can leave your quotes resting in the book while the market price moves.

Use heartbeat-driven recovery on your side:

  1. Track the last frame time for the public market socket and the private account socket independently.
  2. If either socket exceeds your tolerance (say, 30s), closes, or reports a reset or error that makes its view untrusted, pause placements and request cancel-all. Do not treat that request as a fence for a placement that may still be completing.
  3. Follow the full ambiguity and readiness procedure. Cancel a late-discovered order or repeat cancel-all, then resume only after the ambiguous placement and both stream views are reconciled.

The platform sends heartbeat frames every ~10s on both streams, so 30s of silence is a strong signal. See Market stream → reconnect strategy and Errors, retries, and recovery.

GTD (time-bounded orders)

time_in_force: "GTD" plus expiration_unix_seconds rests an order until it fills or expires. Useful for:

  • Orders you want to forget about (no need to cancel — they expire on their own).
  • Strategies where stale liquidity is worse than no liquidity (set a short TTL on every quote as a backstop, and cancel explicitly when a fill after expiry would hurt).

If a fill after expiration_unix_seconds would hurt your strategy, cancel the order yourself well before it expires. An expired GTD order is not removed at the exact expiration time, and it can still fill until it is removed.

  • An expired order resting on the book is removed with an order_cancelled frame with reason: "USER", and GET /trade/v1/orders/{order_id} reports it as CANCELLED.
  • An order still PENDING at its expiration is cancelled without a frame, and GET /trade/v1/orders/{order_id} reports it as CANCELLED.
  • An order still SUBMITTING at its expiration can still be placed. You can receive order_accepted and fills; whatever rests is then removed with an order_cancelled frame. If its submission is rejected or fails instead, it ends REJECTED or FAILED with an order_rejected frame and no order_cancelled, so reconcile whichever terminal status it reaches.

See Orders → time in force.

Streaming for quote rebalancing

Polling the orderbook works but adds latency. For real-time rebalancing, subscribe to the streams:

ChannelUse it for
orderbookFull-depth updates. Current v1 sends a replacement snapshot for every book change; clients must still route by data.kind and apply a delta if one is received. Use the REST-watermark seed plus this channel for quote readiness, fitting a curve, computing fair value, or anything that depends on the shape of the book
best_quoteOptional top-of-book signal with a smaller payload. It carries the best prices and no depth, so never use it as your only book source
tradesPublic fill flow — to detect taker pressure and adjust sizing
account_eventsYour own order, fill, position, redemption, and collateral changes — use these for low-latency updates and reconcile with REST

A trading bot should use orderbook + account_events as its authoritative hot path. Add best_quote as a convenience signal if useful, and use REST /trade/v1/orderbook/{token_id} when recovering or verifying local state.

Sizing against available capital

The /trade/v1/portfolio/summary response carries your collateral balance as cash_balance_micro. It is not reduced by an order that can still become active. Before sizing another BUY, fully page POST /trade/v1/orders/list and calculate the remaining notional and worst-case fee requirement of every nonterminal BUY commitment, including a pre-book order. The portfolio open-orders view also includes active AGARA commitments; whichever endpoint you use, follow every cursor and include the pre-book statuses.

For each nonterminal commitment:

  1. For a LIMIT order, calculate unfilled shares as original_size_micro - size_matched_micro. A LIMIT BUY consumes its remaining notional plus worst-case fee headroom; a LIMIT SELL consumes those outcome shares.
  2. For a MARKET BUY, reserve its collateral_amount_micro. That budget includes the fee headroom and has no original_size_micro or price_micro.
  3. For a MARKET SELL, reserve its unfilled original_size_micro shares. Collateral cannot cover missing SELL inventory.

Read outcome balances through POST /trade/v1/portfolio/positions/list. Do not size an AGARA SELL while unavailable_exchanges includes AGARA; retry the unpaginated positions read before using it.

BUY commitments are checked per market. A conservative strategy should also cap aggregate exposure across every market it quotes.

When an order is rejected

We check your order against your live balance and positions before accepting it, so common quote-loop rejections come back synchronously as a 422 on the POST response. Branch on the stable code field:

CodeWhat it meansTypical reaction
post_only_would_crossThe book moved against your quote before it restedRe-fetch top of book, re-quote
insufficient_balanceNot enough collateral to back a BUYCheck cash_balance_micro and your open orders, retry with smaller size
insufficient_sharesNot enough tokens to back a SELLReconcile inventory, retry with smaller size
order_not_fillableA FOK that couldn't fully fillRe-fetch the book, re-size or re-price
market_closedThe market no longer accepts ordersStop quoting and reconcile open orders

The check is best-effort. Your balance or the book can move between it and the match, so an order can still be accepted (202 / PENDING) and then land as a FAILED or REJECTED order with a typed failure — poll /trade/v1/orders/{id}, or handle the order_rejected event on the account stream when it is delivered. The market_status channel tells you when to stop quoting.

Malformed JSON is 400 malformed_json. A bad time-in-force / post-only combination, or any other malformed request shape, is 422 validation_failed. An order notional outside the 0.10–100,000 collateral-unit bounds is 422 order_value_below_minimum or 422 order_value_above_maximum; the other order bounds each have their own code too.

Don't blanket-retry — most of these need different action, not a re-attempt. Follow the response's recovery instruction in Errors and recovery.

Settlement considerations

This deployment settles AGARA fills on Base using USDC.

  • Settlement state. Keep matched and settled exposure separate. Do not depend on an undocumented confirmation time.
  • Resolution risk. Reduce or stop quoting when your strategy cannot price the approach to resolution safely.
  • Token rotation. PATs don't expire by default, but rotation is a good operational practice. See Authentication → TTL.
  • Withdrawals. Funds remain in the AGARA account until you withdraw them. A self-custody client can submit a supported same-chain withdrawal in an owner-signed account batch. Every path settles asynchronously.

The signed-in web flow can also quote supported cross-chain withdrawal routes. Request a fresh supported-assets response and quote before confirming.