Outcome Integration Guide
Every Outcome endpoint has its own reference page. This guide connects them in the order an integration actually calls them, and flags the three places where the Outcome flow differs from perps.
The short version of those three differences:
- Discovery metadata comes from a gateway aggregation in
snake_case, while the on-chain instrument definition iscamelCase. They describe the same market and are not interchangeable. - Placing an order is asynchronous. A successful HTTP response means queued, not matched, and the terminal result arrives on a different WebSocket endpoint than the matching result.
- A position query returns only positions. Account balances live behind a separate call.
Examples use the TypeScript SDK; every step maps to one
POST /info, POST /exchange, or WebSocket subscription if you are integrating
directly.
The flow at a glance
Fund the Outcome sub-account, place the order, then follow two channels on two endpoints.
A successful order response can mean queued, not matched. Subscribe to both channels in step 4, or a rejected queue entry looks like an order that never filled.
- POST /exchangeAccount-signed
tokenSendMove USDC from
perptooutcome. Account-signed; an agent cannot move funds. - POST /exchangeAgent-signed
orderais theinstrument_id;b/rencode the leg (buy NO =b: false, r: false) andpis that leg's price. TIFGtc,Gtd, orAlo—Iocis rejected.Possibly an async acceptance with a
traceId - Do all of these
- POST /info
outcomePositionsPositions only — no margin summary. Use
clearinghouseStatewithaccountType=outcomefor that.
The sections below take each step in turn, with code.
1. Discover markets
outcomeMeta is the discovery feed: events with nested
markets, live trading gates, pricing, volume, and settlement state.
const page = await sdk.info.outcomeMeta({
exchangeId: 1,
limit: 20,
sortBy: 'volume',
})Two response shapes come out of this one query. An eventSlug point lookup
returns the event object directly; everything else returns a paged envelope.
Narrow before reading events:
import { isOutcomeMetaPage } from 'txflow-typescript-sdk'
const events = isOutcomeMetaPage(page) ? page.events : [page]
const market = events[0]?.markets[0]Page size caps at 50; walk with next_cursor for more. sortBy: 'endingSoon'
is an ascending semantic order that carries its own direction, so send it
without sortOrder.
Resolving a deep link works the other way round — look the event up by the market slug in your URL:
const result = await sdk.info.outcomeMeta({
exchangeId: 1,
marketSlugs: ['btc-up-or-down-5m'],
markets: 'matched',
})markets: 'matched' returns only the requested markets; 'event' returns every
market in each owning event.
market.instrument_id is the identifier every later step needs.
2. Stream quotes
For a grid or watchlist, outcomeBbos batches best
bid and ask across many markets on one subscription:
await sdk.subscriptions.outcomeBbos(
{ instrumentIds: [market.instrument_id], exchangeId: 1 },
({ bbos }) => {
for (const { instrumentId, bbo } of bbos) {
const [bid, ask] = bbo
console.log(instrumentId, bid, ask)
}
},
)One subscription carries 1–50 instrument ids and one connection accepts up to
20 active groups. Beyond that, open more subscriptions on the same connection.
Either side of bbo is null when the book is empty there.
For a single market's full depth, the shared
l2Book channel applies. Outcome markets are addressed
by instrument id as a decimal string, not the OUTCOME-<id> name:
await sdk.subscriptions.l2Book(
{ coin: String(market.instrument_id) },
({ levels }) => {
const [bids, asks] = levels
console.log(bids[0]?.px, asks[0]?.px)
},
)The same string rule applies to trades and
activeAssetCtx. To follow several markets'
tapes at once, use outcomeTrades, which takes
numeric instrumentIds instead.
For charts, outcomeProbability returns the
historical YES probability series. To keep the chart live, subscribe to
candle on /api-ws with the instrument id as coin
and append its closes, which are YES probabilities.
3. Fund the sub-account
Outcome trades settle in a dedicated sub-account. Move USDC into it with an
owner-signed tokenSend:
await sdk.exchange.tokenSend({
token: 0,
amount: '100',
fromAccountType: 'perp',
toAccountType: 'outcome',
})4. Place an order, and learn what happened
Orders use the shared order action with the instrument
id as asset:
const ack = await sdk.exchange.placeLimitOrder({
asset: market.instrument_id,
side: 'buy',
px: '0.55',
sz: '10',
})| Channel | Endpoint | Answers |
|---|---|---|
outcomeOrderUpdate (singular) | /api-ws | Did the queued order fail before reaching the node? Pushed only on failure. |
outcomeOrderUpdates (plural) | /ws | What did the book do — filled, cancelled, rejected? |
// Delay-queue result, correlated by traceId.
await sdk.apiSubscriptions.outcomeOrderUpdate({ user }, (result) => {
if (result.done && !result.success) {
console.error('never reached the node', result.traceId, result.body?.message)
}
})
// Matching-engine lifecycle.
await sdk.subscriptions.outcomeOrderUpdates({ user }, (updates) => {
for (const { order, status } of updates) {
console.log(order.oid, order.outcome, status)
}
})Watching only the plural channel makes a rejected queue entry look like an order that silently never filled. The singular channel stays silent when the order goes through, so it never tells you whether it traded.
order.outcome is the displayed leg (Yes or No); order.side (B or A)
and reduceOnly are the wire b and r (see
Outcome orders).
Cancelling uses the shared cancel action.
5. Read positions and balances
This is the second place the Outcome flow surprises people:
outcomePositions returns only the
assetPositions array. There is no margin summary and no withdrawable.
const [positions, account] = await Promise.all([
sdk.info.outcomePositions({ user }),
sdk.info.clearinghouseState({ user, accountType: 'outcome', exchangeId: 1 }),
])| Need | Call |
|---|---|
| Open positions | outcomePositions |
| Account value, margin, and withdrawable balance | clearinghouseState with accountType: 'outcome' |
| Active orders | outcomeFrontendOpenOrders |
The YES and NO legs of one market are two separate records; the sign of
position.szi distinguishes them.
One asymmetry to keep in mind: outcomePositions accepts an optional
exchangeId, outcomeFrontendOpenOrders does not.
History endpoints need an explicit opt-in. Add 'outcome' to accountType on
userFills and
historicalOrders, or to accountTypes on
userNonFundingLedgerUpdates — note the
plural field name and the singular 'perp' spelling on that last one.
6. Split and merge complete sets
outcomeOperate converts between collateral and
a complete share set, without touching the book:
// Lock collateral, receive symmetric YES and NO positions.
await sdk.exchange.outcomeOperate({
split: [{ asset: market.instrument_id, sz: '10' }],
})
// Return a symmetric pair, release the collateral.
await sdk.exchange.outcomeOperate({
merge: [{ asset: market.instrument_id, sz: '10' }],
})Exactly one of split or merge is present per action. This is useful for
market making one leg while hedging the other, and for exiting when one side has
no bid.
7. Settlement
Settlement state travels on the market object from step 1:
const settlement = market.settlement
if (settlement?.resolution_status === 'resolved') {
console.log('winner:', settlement.winning_outcome)
}resolution_status is prepared or resolved. winning_outcome is derived
from outcomes and settlement_fraction after resolution — it is the
protocol's result, not the metadata provider's oracle_outcome. Poll
outcomeMeta for the market slug to refresh it.
Recurring series
Crypto up/down markets repeat on a fixed cadence.
outcomeSeriesTimeline returns the past,
current, and upcoming windows around an anchor event so you can build a period
navigator, and cryptoPrice /
cryptoPriceHistory supply the price-to-beat and
the chart series.
Unlike outcomeMeta, the timeline response is camelCase.
What is out of scope
- Creating markets.
outcomeDeployis permissioned — see Exchange API. - Risk-notice acknowledgement. The first-party app records this through application-session endpoints that are not part of this API surface, as noted in TIP 3. Present disclosures appropriate to your own product and jurisdiction.
Next steps
outcomeMetareference — full discovery parameters- TIP 3 — the protocol model behind these endpoints
- SDK Subscriptions — reconnection and replay