Every message has one job.
The payload catalog separates connection control, complete state, market deltas, and failures. The small vocabulary is intended to keep hot-path routing predictable.
Catalog
session.readyAuthentication accepted and session capabilities established.CONTROLsubscription.readyFilters accepted and a subscription ID assigned.CONTROLstate.snapshotComplete subscription state at one sequence.STATEodds.upsertCreate or fully replace one current quote.DELTAodds.removeRemove one quote from current state.DELTAevent.updateApply a small event lifecycle change.DELTAsession.heartbeatConfirm liveness and the latest stream position.CONTROLrequest.errorDescribe a rejected client action.CONTROLMessage envelope
{
"type": "odds.upsert",
"schema_version": "0.3",
"message_id": "msg_01J5Y7M4T6",
"sequence": 18492,
"emitted_at": "2026-08-07T05:20:00.438Z",
"data": { ... }
}typestringPayload discriminator.schema_versionstringContract version required to decode the message.message_idstringUnique tracing and deduplication identifier.sequenceintegerOrdered stream position.emitted_atRFC 3339Time OddsLoom emitted the frame.dataobjectShape selected by type.state.snapshot
Complete state for the accepted filters. Replace local subscription state atomically, commit its sequence, then process later deltas.
{
"type": "state.snapshot",
"schema_version": "0.3",
"message_id": "msg_01J5Y7M0KJ",
"sequence": 18491,
"emitted_at": "2026-08-07T05:20:00.120Z",
"data": {
"subscription_id": "sub_01J5Y7KZXN",
"sports": [ ... ],
"leagues": [ ... ],
"participants": [ ... ],
"events": [ ... ],
"markets": [ ... ],
"books": [ ... ],
"quotes": [ ... ]
}
}odds.upsert
Create or replace the quote with this ID. The payload is deliberately a full quote rather than a partial patch.
{
"type": "odds.upsert",
"schema_version": "0.3",
"message_id": "msg_01J5Y7M4T6",
"sequence": 18492,
"emitted_at": "2026-08-07T05:20:00.438Z",
"data": {
"subscription_id": "sub_01J5Y7KZXN",
"quote": {
"id": "quote_book_a_market_123_outcome_min",
"market_id": "market_evt_min_den_full_game_spread",
"outcome_id": "outcome_min",
"book_id": "book_a",
"availability": "open",
"line": 5.5,
"price": { "american": -105, "decimal": 1.9524 },
"observed_at": "2026-08-07T05:20:00.432Z"
}
}
}odds.remove
Delete the quote from current state. Temporary unavailability should normally arrive as an upsert with availability: suspended, not removal.
{
"type": "odds.remove",
"schema_version": "0.3",
"message_id": "msg_01J5Y7M8PQ",
"sequence": 18493,
"emitted_at": "2026-08-07T05:20:01.014Z",
"data": {
"subscription_id": "sub_01J5Y7KZXN",
"quote_id": "quote_book_a_market_123_outcome_min",
"reason": "offer_withdrawn",
"observed_at": "2026-08-07T05:20:01.009Z"
}
}quote_idstringQuote to remove.reasonenumoffer_withdrawn, market_closed, event_closed, correction, or source_unavailable.observed_atRFC 3339Time the removal condition was observed.event.update
Updates event lifecycle fields without resending its participants or market definitions. Only keys present in changes are replaced.
{
"type": "event.update",
"schema_version": "0.3",
"message_id": "msg_01J5Y7N13C",
"sequence": 18494,
"emitted_at": "2026-08-07T05:20:02.201Z",
"data": {
"event_id": "event_nba_20260808_min_den",
"changes": {
"phase": "live",
"status": "active"
},
"observed_at": "2026-08-07T05:20:02.190Z"
}
}session.heartbeat
A heartbeat proves the connection is live and advances no domain state. Its envelope sequence reports the latest position known to the session.
{
"type": "session.heartbeat",
"schema_version": "0.3",
"message_id": "msg_01J5Y7QHB4",
"sequence": 18512,
"emitted_at": "2026-08-07T05:20:15.000Z",
"data": { "session_id": "ses_01J5Y7K1Y7" }
}request.error
A request-scoped failure does not necessarily close the session. Consumers should branch on code and retryable, not parse message text.
{
"type": "request.error",
"schema_version": "0.3",
"message_id": "msg_01J5Y7RXE0",
"sequence": 18513,
"emitted_at": "2026-08-07T05:20:16.104Z",
"data": {
"request_id": "req_01J5Y7RX8V",
"code": "INVALID_FILTER",
"message": "Unknown league key.",
"retryable": false,
"details": { "field": "leagues", "value": "nbaa" }
}
}