Skip to main content
V3 is currently live on testnet only. Do not point a production integration at V3 until August 25th at 10:00 AM EST — until then, target V3 on testnet and keep production on V2.
This guide is for developers with a working V2 client. For the reasoning behind the changes, see Improvements. For a new integration, start at Quickstart.

What’s fundamentally different

Five changes underlie the rest of this guide:
  1. Capital moves to a proxy wallet. You deploy and fund a secure proxy. A new prerequisite with no V2 equivalent.
  2. One endpoint to make and take. Maker and taker both POST to /orders-v3. timeInForce (GTC rests; IOC/FOK execute immediately) determines which you are.
  3. API keys are required on every authenticated request — and the header is renamed: V2 X-Api-Key → V3 x-sx-api-key. Search-and-replace it everywhere.
  4. Refund and capital efficiency events are in-lined. No longer separate events or endpoints
  5. Order signing process has changed
  6. pendingFills fields have been removed.

What has not changed

  • percentageOdds is still implied probability × 10^20; totalBetSize is still your stake in base-token units (USDC = 6 decimals). See Unit conversions.
  • The odds ladder is still enforced (default step 0.125%). Read oddsLadderStepSize from GET /metadata/obv3. See Odds rounding.
  • Resting orders are still quoted in the maker frame; taker implied probability = 1 − maker probability. (showTakerPerspective=true on the orderbook snapshot does the inversion for you.)
  • You still sign with your EOA private key, and maker is still your EOA address.
  • Market discovery is unchanged: GET /markets/active, GET /markets/find, and the sports / leagues / fixtures endpoints and shapes.
  • The { "status": ..., "data": ... } response envelope is unchanged.
  • The realtime endpoint and protocol are unchanged (wss://realtime.sx.bet/connection/websocket, Centrifugo), and the fixtures:*, markets:global, main_line:global and parlay_markets:global channels are unchanged.
  • Testnet hosts are unchanged: https://api.toronto.sx.bet and wss://realtime.toronto.sx.bet/connection/websocket.

Step-by-step guide

1

Set up the account: deploy and fund a proxy wallet

Deploy a proxy your account owns and move USDC into it — see Accounts. Easiest way is just login to sx.bet and go through the wizard.Prefer to stay in code? The whole flow is API-first too (all with the x-sx-api-key header):
Funding the proxy replaces V2’s “enable betting” step. There is no longer an ERC-20 approval of the TokenTransferProxy (POST /orders/approve is gone)Already live on V2? Your trading capital currently sits in your EOA under V2’s model; to trade on V3 it must be inside the proxy. Login to your SX Bet account and follow the pop-up wizard to migrate.
All V2 orders at cutoff will be auto-cancelled. V2 clients will fail to run after V3 goes live at 10AM EST on August 25.
2

Get a new API key

Create a new API key — your existing V2 key will not work. Log in at sx.bet, open Account → Overview, generate a key, and send it in the x-sx-api-key header on every authenticated request. See API Keys.
This key is more sensitive than before, as it can now cancel all your orders.
3

Use POST /orders-v3 for posting orders and fills

  • There is no longer a separate endpoint for fills. The endpoint is POST /orders-v3 timeInForce is new and required and the signature has changed. timeInForce implies if your order will rest or not.
  • If you’re a market maker, use GTC. If you’re a taker, use IOC. See Time in force.
  • The signing of the order has also changed. See the example below.
  • The old apiExpiry field was eliminated and merged into just a single expiry field. In V3 expiry is a real unix-seconds timestamp inside the signature (0 = never). Because it is signed, changing an order’s TTL means re-signing. V2’s constant 2209006800 is gone — do not send it.
  • The V2 slippage vocabulary is also gone — there are no desiredOdds or oddsSlippage fields, and no ODDS_STALE error. percentageOdds is the worst price you will accept; the engine matches at that price or better. See Odds.
  • This endpoint, by default, is now asynchronous, meaning that the outcome of your order (filled / partially filled / cancelled) is not known immediately. Use waitForOutcome to keep the old synchronous behaviour. This applies to both orders that will match, clear, or rest.
Field-by-field, what changed on the order body from V2 (POST /orders/new and POST /orders/fill/v2) to V3 (POST /orders-v3):marketHash, maker (your EOA, never the proxy), baseToken, totalBetSize, percentageOdds, salt, and isMakerBettingOutcomeOne are unchanged. The EIP-712 domain version is now "1", not "1.0".
The top-level status (PENDING/FAILED) is the accept/reject of your submission and is independent of outcome.state. outcome is present only because waitForOutcome was set; without it the call returns as soon as the order is accepted and you track the result on the account:orders_v3 channel instead.Worked example — the two frames: you post GTC with percentageOdds = "52500000000000000000" (52.5% — your implied probability on your own outcome). A taker who wants the other outcome sees your level as 47.5% under showTakerPerspective=true and submits their own order with percentageOdds = "47500000000000000000" as their worst acceptable price.Full new signing guide: EIP-712 order signing.
4

Taking liquidity

As mentioned above, the fill endpoint is gone. Taking is just submitting an order with timeInForce: "IOC" / "FOK". percentageOdds is the worst price you will accept.
An IOC order fills what it can and cancels the rest, so a partial fill returns outcome.state: "PARTIAL_FILL_DONE" with the unmatched remainingAmount cancelled (a FOK order instead fills completely or cancels whole). Drop waitForOutcome and the call returns as soon as the order is accepted (status: "PENDING", no outcome), and you track fills on the account:fills_v3 channel instead.
5

Adjust cancel actions

Cancels no longer have an extra signature. In V3 all three are just DELETEs authenticated by your API key. CancelOrderV2SportX / CancelOrderEventsSportX / CancelAllOrdersSportX EIP-712 domains are now gone.
Cancel-by-id waits for the engine and reports per order; cancel-by-event and cancel-all are asynchronous and only confirm the job was published. See Cancelling orders.
6

Re-arm your heartbeat (dead-man switch)

Endpoint renamed: V2 POST /heartbeat → V3 POST /heartbeat/v3. V2’s requestor field is gone (the timer is keyed to your API key’s account):
Semantics are unchanged: miss the window and every open order is cancelled, arriving on the account:orders_v3 channel as inactiveReason: "HEARTBEAT_TIMEOUT". Refresh well inside the window (e.g. every 20s on a 60s timeout).To disarm, call the same endpoint with timeoutSeconds: 0 — that clears an armed timer. There is no separate cancel route; V2’s POST /heartbeat/cancel has no V3 successor. See Heartbeat.
7

Realtime changes

The token route: point it at GET /user/realtime-token-v3/api-key (V2 used GET /user/realtime-token/api-key). Adjust channel names and a few payload keys.
The addresses in the channel keys are your EOA / user address checksummed and NOT your proxy wallet address.
fixtures:*, markets:global and main_line:global are unchanged.The order book channels are more than a rename — they no longer stream individual orders. See the next step, Re-model the order book feed, for the new anonymous, aggregated shape.
Delivery is at-least-once, so a message can be replayed. De-duplicate on the client using the messageId in ctx.tags — drop any publication whose messageId you have already seen. See Realtime reliability.The orderbook channels (orderbook_v3 and orderbook_v3_event) are the exception: use their version field to order and de-duplicate updates within a market — apply a publication only when its version is strictly greater than the one you hold. See Book versioning.
Full reference: Real-time data.
8

Re-model the order book feed

In V2, order_book:market_{marketHash} published a stream of individual order rows/deltas you merged into a local book. In V3, orderbook_v3:{marketHash} (and orderbook_v3_event:{eventId}) publish the entire resting book on every update, aggregated by price into anonymous levels.
  • No per-order identity. Levels carry only percentageOdds and aggregate size. Your own orders still arrive individually on account:orders_v3_#{address}.
  • Replace, don’t merge. V2 required upsert/remove by orderHash; every V3 publication is the complete book for that market.
  • Version-gate instead of updateTime. Each publication carries a version (a single monotonic string). Apply a message only when its version is strictly greater than the one you hold for that market. See Book versioning.
  • Sides are explicit. Levels are split into outcomeOne / outcomeTwo (always the maker frame), replacing V2’s per-row isMakerBettingOutcomeOne. There is no showTakerPerspective on this channel (unlike GET /orderbook-v3/snapshot); invert yourself with takerOdds = 1 − percentageOdds / 10^20.
Before/after payloads:
Subscribe first, then seed from REST inside the subscribed handler, and run both the snapshot and every publication through the same version rule so ordering never depends on timing. When the channel replays the book from history (recovered: true), skip the seed entirely. Full pattern: Seeding and subscribing to the order book.
Full reference: Order book updates and Event order book updates.
9

Parlay RFQ market makers

Nothing changes about how you hear requests: subscribe to parlay_markets:global exactly as in V2.What changes is how you respond. Price the request, then post your quote to POST /orders-v3 with timeInForce: "GTC" against the parlay marketHash, signed the new way (see the posting step above). Use a real expiry no later than the parlay market’s own expiry — expiry: 0 is rejected on parlays. Full walkthrough: Market making parlays.
10

Adjust query endpoints

We have renamed the data types to more intuitive names, and adjusted the endpoints. These endpoints all require an API-key and you can only see your own activity.See Grain types.Three changes apply across all four:
  • Rsults are bound to your API key, so V2’s bettor=... and maker=true query params are gone.
  • Pagination is now uniform: perPage + nextKey cursor everywhere.
  • V2’s settled=true/false becomes the status filter (PENDING / LOCKED / SETTLED / FAILED).
Field-level diffs are on each reference page.Identifier changes. If your V2 client keys off orderHash or fillHash, remap it.
11

Read refunds from rows, not a refund endpoint

There is no refund endpoint or refund channel in V3. Capital-efficiency refunds are republished as fields inline on the trade and fill rows you already read — not as a separate resource or event. (If you never consumed CE refunds in V2, there is nothing to do here.)
12

Understand the new order and trade state machines

Order statusPENDING → ACTIVE → INACTIVE (IOC/FOK orders skip ACTIVE and go straight to INACTIVE):FILLED is an inactiveReason, not a status: a fully filled order is status: "INACTIVE" with inactiveReason: "FILLED". See Order lifecycle.Bet statusPENDING → LOCKED → SETTLED, or PENDING → FAILED:See Bet lifecycle.

Endpoint changes

Rate limits have changed

V2 limited endpoint groups (all POST /orders/* shared 5,500/min; all GET /trades/* shared 200/min). V3 limits are per-endpoint. Relevant changes: Full list, response headers, and the global bucket: Rate limits.

Migration checklist

Run it on testnet (api.toronto.sx.bet) first.
  • Create an API key. Apply the key on every privileged endpoint.
  • Deploy and fund the proxy.
  • Fetch GET /metadata/obv3; wire domain, activeAsset.baseToken, oddsLadderStepSize and limits.* into config; delete the executorAddress lookup.
  • Re-implement order signing.
  • Post a minimum-size GTC with waitForOutcome: true; then take it with an IOC from a second account.
  • Port cancels to the three DELETEs; delete the V2 cancel-signing code; test “by id”, “by event”, and “all”.
  • Move realtime: new token route, channel renames per the “Realtime changes” step, messageId dedup, and version-gating on the orderbook channels.
  • Point accounting at /trades-v3 / /fills-v3 / /positions-v3; remap orderHash / fillHash ids; switch pagination to perPage + nextKey.
  • Arm POST /heartbeat/v3 and let it lapse once on testnet to watch HEARTBEAT_TIMEOUT cancel your orders.

Improvements

What changed in V3.

Quickstart

The full V3 flow, metadata to placed bet.
Last modified on August 14, 2026