UTF-8 JSON, snake_case fields. Request gzip with curl --compressed.
Identifiers
cp_… competition, ev_… event, mk_… market. Opaque, stable and never reused. Sport IDs are lowercase slugs.
Time
RFC 3339 UTC timestamps with milliseconds, e.g. 2026-10-24T14:23:52.004Z.
Versions
Integer values below 253. Compare exactly; do not interpret them as timestamps.
Compatibility
Ignore unknown fields/message types. Ignore markets with unknown types, periods or outcome keys. Treat unknown market states as suspended and unknown event statuses as not tradable.
Run in Bash. On macOS, enter bash first if your shell is zsh. Put the supplied API trust certificate in certs/api-ca.pem relative to the current directory.
Keep keys out of URLs and checked-in files. The commands below use the environment variables set here; the documentation does not make authenticated requests from your browser.
/v1/health requires no API key, but network allowlisting still applies. An ok health response does not establish an AMQP join or make a cached price usable.
Place the supplied certificates in certs/. Replace yourclient and 1 with your assigned client ID and queue set. The example below selects football prices and event objects.
For this example the queues are c_yourclient.prices.1 and c_yourclient.results.1. They must already exist. The client binds the prices queue; the operator has already bound heartbeats and results.
kind messages are the example’s diagnostic output, not the raw AMQP payload. A successful join may contain zero rows. A later joined:false suspends every previously printed price.
4. Read prices through the suspension gate
In an application, use the consumer’s price(market_id) method when reading a price. It returns a suspended view with null probabilities while disconnected or stale. Reading the raw row cache bypasses this protection.
Keep FEED_RESULTS_FILE across restarts. Each active or standby process needs its own file. The journal is an example durable sink; a production application can replace it with a database transaction while preserving the store-before-ACK rule.
Environment variable reference
Variable
Meaning / default
FEED_BINDINGS
Comma-separated supported keys. Default #.
FEED_EVENTS
1 keeps event objects; default 0.
FEED_UNBIND
Old keys to remove from both price and event exchanges when changing selection.
FEED_QUEUE_SET
Assigned queue pair number; default 1.
FEED_RESULTS_FILE
Durable result journal; default results.jsonl.
FEED_API_CA_FILE
API trust certificate. Required for the supplied private-certificate endpoint.
Discover IDs from the API, then use those IDs in price filters and RabbitMQ bindings. Sports and competitions appear only when they have listed events.
sport: football
└─ competition: cp_…
└─ event: ev_…
├─ market: mk_… 1x2 / match
└─ market: mk_… total / match / 2.5
List sports
GET/v1/sports
Returns every sport with at least one listed event. Not paginated. The scheduled/live counts describe events, not open markets.
Returns competitions with listed events, sorted by name. Use sport for a comma-separated list of sport slugs. country is null for international competitions.
Comma-separated sport slugs, e.g. football,tennis.
competition
Comma-separated competition IDs.
status
Comma-separated statuses. Default scheduled,live; all statuses by default when updated_since is supplied.
from / to
Scheduled start at or after from, before to. UTC timestamps.
updated_since
Events whose updated_at is at or after this timestamp, including market-status and result changes.
limit / cursor
Page size and continuation token; see pagination below.
Different filters combine with AND. Values within one comma-separated filter combine with OR. Events are sorted by scheduled_start, then id. Markets are not included in this list response.
BASH
# Replace this with an ID from /v1/competitions.
COMPETITION_ID='cp_01KAZ000000000000000000001'
curl --silent --show-error --fail-with-body --compressed --get \
--cacert "$FEED_API_CA_FILE" \
--header "Authorization: Bearer $FEED_API_KEY" \
--data-urlencode "competition=$COMPETITION_ID" \
--data-urlencode 'status=scheduled,live' \
--data-urlencode 'limit=100' \
"$FEED_API_URL/v1/events"
Names and stable home/away roles. In tennis, “home” identifies a fixed participant; it is not a venue designation.
scheduled_start
Current scheduled start. Can change after postponement or for resumption.
started_at / ended_at / resolved_at
Nullable lifecycle timestamps. Finished and fully settled are different states.
live
Football score/period/clock, or null. Its individual fields can also be null.
updated_at / version
Synchronization timestamp and monotonic event version. Compare versions when merging event copies.
For a running clock, add elapsed time since clock.as_of to clock.seconds. Freeze it when running:false. Do not infer market availability from a running clock.
Get an event and its markets
GET/v1/events/{event_id}
Returns the event fields plus a markets array. Optional market_types and periods are comma-separated; every market type and period match are the defaults.
Request the first page with your filters and limit (default 100; capped at 500).
Process data. If next_cursor is not null, pass it as cursor with exactly the same filters.
Continue until next_cursor:null. Tokens are opaque and valid for 10 minutes.
BASH
# jq extracts the token returned by your previous request.
NEXT_CURSOR=$(jq -r '.next_cursor // empty' competitions-page.json)
if [ -n "$NEXT_CURSOR" ]; then
curl --silent --show-error --fail-with-body --compressed --get \
--cacert "$FEED_API_CA_FILE" \
--header "Authorization: Bearer $FEED_API_KEY" \
--data-urlencode 'sport=football' \
--data-urlencode 'limit=100' \
--data-urlencode "cursor=$NEXT_CURSOR" \
"$FEED_API_URL/v1/competitions"
fi
Save the first competitions response as competitions-page.json before running this continuation example. For event synchronization, use updated_since, read every page and merge by event ID/version. Avoid restricting it to scheduled,live when you need ended or cancelled events.
Final events older than 7 days with all markets settled, or ended/final events older than 30 days, may no longer be served. Their event and market detail endpoints can return 404. Keep the results your application needs.
Supported market types and their interpretation. Actual event and competition availability comes from the catalogue endpoints.
Supported sports
Sport ID
Markets
Pricing
football
1x2, total, handicap
Pre-match and live
tennis
moneyline
Pre-match only
basketball · NBA
moneyline, total, handicap
Pre-match only
ice_hockey · NHL
moneyline, total
Pre-match only
For tennis, NBA and NHL, every market is suspended with reason match_event from confirmed play or the scheduled start, whichever comes first. Event statuses and results continue. A sport enum appearing in a schema is not a promise that the sport is available.
Competition availability
Use GET /v1/competitions?sport=football for the competitions currently listed. Store the returned ID and name; bind by ID. A fixed league-name list cannot tell you which competitions have events today.
Tennis is grouped under men’s and women’s competitions; do not assume coverage is limited to a particular tour. NBA and NHL are the supported basketball and ice hockey leagues. A listed competition does not imply that every fixture, market type or line is currently available.
Football competition examples
The released coverage includes the competitions below. This is not an exhaustive list or a live availability report; discover current competition IDs through the API.
Country / region
Competition names
England
Premier League, EFL Championship, EFL League One
Germany
Bundesliga, 2. Bundesliga
Spain
La Liga, Spanish Segunda División
Italy
Serie A, Italian Serie B
France
Ligue 1
Netherlands
Eredivisie
Scotland
Scottish Premiership
Europe
UEFA Champions League, UEFA Europa League, UEFA Conference League
USA / Canada
MLS
Mexico
Liga MX
Brazil
Brazilian Série A
Colombia
Colombian Primera A
Japan
J1 League
Australia / New Zealand
A-League Men
Egypt
Egyptian Premier League
Market types and outcomes
Type
Outcome keys
Line / interpretation
1x2
home, draw, away
line:null. Three-way match result.
moneyline
home, away
line:null. Match winner; two outcomes.
total
over, under
E.g. line:2.5: over wins above 2.5; under wins below 2.5.
handicap
home, away
Line belongs to the home side. At -1.5, home must win by at least 2; away has the opposite line.
The only period in this release is match. Total and handicap lines are whole numbers plus 0.5; no supported line can push. Discover lines from returned markets rather than constructing IDs from type and line.
Market status versus price state
Field
Values
Client behavior
market.status
active, removed, settled
Listing lifecycle. Removed means withdrawn; settled means a result is available.
state
open, caution, suspended
Price usability. Caution has increased uncertainty; suspended has no usable probability.
Consumer join state
Joined or suspended locally
Applies to the entire cached view. An open row cannot be used while the consumer is unjoined or stale.
Event statuses
Status
Meaning
scheduled
Not started.
live
In play; an interrupted match awaiting resumption stays live.
ended
Finished, but not all results are known.
resolved
Finished; every market has a result. Final.
postponed
Will not start at its current scheduled time.
cancelled
Will not take place; markets are void. Final.
abandoned
Stopped after play started; apply the sport’s settlement rules. Final.
live is not evidence that a price is usable. Always check the consumer gate, market status and price state.
Each price row contains one market, its event, named outcomes, its current price and its result. A row is a complete replacement, not a partial patch.
Get current prices
GET/v1/prices?sport=football
Without since, the response contains markets that are not removed for scheduled or live events. It is not paginated. Optional filters: sport, competition, event, market_types, periods. Fields combine with AND; comma-separated values combine with OR.
The example since value is a placeholder. Use the token returned by your own request.
PriceRow fields
Field
Type
Meaning
market_id
string
Stable row key. Replace stored copies by this key.
market
object
type, period, nullable line, listing status.
event
object
ID, display name, sport, competition ID/name, status and scheduled start. It is a compact event, without participants or live clock.
state / reason
string / string or null
Usability and reason. Reason is null for open markets.
outcomes[].key / name
string
Stable outcome key and display label. Match results by key, not label.
outcomes[].prob
number or null
Fair probability in [0.0001, 0.9999], to 4 decimals. Non-null probabilities sum to 1.
outcomes[].fair_odds
number or null
1 / prob, rounded to 3 decimals.
uncertainty
number or null
Uncertainty in probability units, to 3 decimals. 0.008 means 0.8 percentage points of probability.
ts
UTC timestamp
Price timestamp; not a message sequence or client freshness test.
result
object or null
Winning key / void, revision and resolution time.
version
integer
Increases when any part of this row changes. Preserve the full integer.
In the example, prob:0.58 is a 58% probability and fair_odds:1.724 is its reciprocal after rounding. Use state and the consumer’s freshness gate before using either value.
Poll changes with since
BASH
# Save the first /v1/prices?sport=football response as prices.json.
SINCE=$(jq -r '.since' prices.json)
curl --silent --show-error --fail-with-body --compressed --get \
--cacert "$FEED_API_CA_FILE" \
--header "Authorization: Bearer $FEED_API_KEY" \
--data-urlencode 'sport=football' \
--data-urlencode "since=$SINCE" \
"$FEED_API_URL/v1/prices"
Replace only returned rows by market_id. Keep other rows. Save the new since.
full:true
Replace the whole view. A full response can also contain changed removed/final rows; process their results and do not use their prices. Save the new since.
A valid token lasts 10 minutes by default and belongs to its original filters. After expiry, filter changes or a service restart, expect a full replacement. A malformed token is rejected with 400. A valid delta can also become a full response when more than half of the current rows changed.
Rows missing from a full replacement may have ended or been removed. Fetch known market IDs when reconciling results. Changes that move a row outside your filters are not guaranteed to appear in REST deltas; refresh the full view to remove them.
Suspension and caution reasons
Reason
Meaning
match_event
A match event affects pricing, or a pre-match-only sport has started.
unstable
Price moving quickly or not yet settled.
low_confidence
Price less certain than usual.
insufficient_data
Not enough data to price the market.
starting
Newly listed market or feed restart.
event_not_active
Event is not scheduled or live.
maintenance
Planned maintenance.
unstable and low_confidence can be caution or suspended. Suspended rows have null probabilities, fair odds and uncertainty. The reference consumer may additionally use the local reason disconnected; that is not a server reason.
Every poll counts against the key’s REST request budget. A response over 2,000 rows is a large answer: at most one per key per 10 seconds by default. Respect Retry-After on 429 and 503, retaining your current since for the retry. Use streaming delivery when frequent large responses prevent the required update rate.
Connect with AMQP 0-9-1 over TLS, consume the assigned queues, then join an HTTPS snapshot before exposing prices.
Queues and exchanges
Exchange
Type
Delivery
Consumer acknowledgment
feed.prices
topic
Complete changed market rows → prices queue
Automatic
feed.events
topic
Optional full event objects → prices queue
Automatic
feed.heartbeat
fanout
Feed heartbeat → prices queue (already bound)
Automatic
feed.results
topic
All results/corrections → results queue (already bound)
Manual, after durable storage
Queues are c_<client>.prices.<n> and c_<client>.results.<n>. Open separate channels for prices and results. Set the AMQP connection heartbeat to 10 seconds and results prefetch between 1 and 1,000.
The operator creates exchanges and durable queues. Do not declare, delete or purge them, and do not change result bindings. Every queue has one active consumer; an additional consumer is standby. Use an independently assigned queue set for each independent price view.
Routing keys and supported bindings
Price, event and result routing keys have the form <sport>.<competition_id>.<event_id>. Only the four binding shapes below are supported for your prices queue.
Selection
Binding
Matching join request
Everything
#
{"filters":[{}]}
One sport
football.#
{"filters":[{"sports":["football"]}]}
One competition
*.cp_ID.*
{"filters":[{"competitions":["cp_ID"]}]}
One event
*.*.ev_ID
{"filters":[{"events":["ev_ID"]}]}
Replace cp_ID / ev_ID with discovered IDs. Multiple bindings select their union. Bind the same selection to feed.events when event objects are needed.
BASH
# Football prices plus one specific event; event ID is illustrative.
export FEED_BINDINGS='football.#,*.*.ev_01KAZ000000000000000000002'
export FEED_EVENTS='1'
There is no routing-key component for market type or line. If your application only needs totals, filter complete market rows locally. The join must still match the queue’s selection.
HTTPS join endpoint
POST/v1/prices/join
Requires an AMQP-enabled API key, JSON content type and Accept-Encoding:gzip. The filter array is a union: separate filter objects combine with OR; fields within one filter combine with AND. Empty filter {} selects everything.
The answer contains a snapshot version and complete rows in data. It is always gzip-compressed. There is no pagination, since or full flag. Calling this endpoint alone does not complete an AMQP join; follow the buffering procedure below.
Initial join procedure
Suspend the entire price view. Connect, open two channels, bind prices/events, start automatic-ACK consumption on prices and manual-ACK consumption on results.
Buffer prices and events in arrival order. Record the time each message is taken from the AMQP client library.
Wait for a fresh non-final heartbeat. It must be received within 3 seconds of sent_at. Record session and seq; discard buffered messages up to and including that heartbeat.
Fetch the join snapshot while continuing to buffer. Use the union of your binding filters. If you need event objects, read all event pages and retain matching events.
Install the snapshot, then replay buffered messages in order. Apply version and heartbeat checks. Only expose prices after replay completes without invalidating the join.
While the HTTPS join and event-page requests run, continue checking every heartbeat and the 3-second silence deadline. A changed session, sequence gap, final/stale heartbeat or silence invalidates the pending join; discard that attempt and restart from a fresh heartbeat.
A missing fresh heartbeat leaves a standby consumer suspended. If no heartbeat arrives within 10 seconds, back off and keep waiting. For join 429/503 responses, honor Retry-After; for network failure, a request over 30 seconds or another 5xx, retry with jittered 1–30 second backoff. After 60 seconds without a join answer, discard the buffer and start from a fresh heartbeat.
Changing the selection
Bindings persist in RabbitMQ. Suspend first, remove obsolete keys from both feed.prices and feed.events, reconnect, bind the new selection and join again. Set FEED_UNBIND to the obsolete keys when using the examples. Never purge a queue to change selection.
Raw broker bodies are UTF-8 JSON. The top-level type identifies the message. These examples show the broker payload, not the consumer’s diagnostic stdout.
price — full market row
Exchange:feed.prices. Body is a REST PriceRow plus "type":"price". Prices are current state; intermediate updates may be coalesced. Do not use this channel as a historical tick log.
The outer type is the message type; market.type is the market type. Replace by market_id only when version is newer. See the field table for probability and state semantics.
event — full event object
Exchange:feed.events. Opt in by binding your prices queue to this exchange. Body is the catalogue Event plus "type":"event". This is where participants and football live score/clock are available.
Replace event objects by ID and version. Clock updates are not a per-second timer: an event update is sent when score, period or clock running state changes. Derive the display clock from seconds, running and as_of.
result — settlement or correction
Exchange:feed.results. Persistent delivery to your results queue. The body is a complete row plus "type":"result", including a non-null result. Results include every sport, irrespective of price bindings.
Redelivery is expected. Apply only a newer row version, persist it, then ACK. result.revision starts at 1 and increases for a correction. Details are in Results and settlement.
heartbeat — feed freshness
Exchange:feed.heartbeat. Fanout, empty routing key, already bound to the prices queue. The feed normally sends one each second.
Maintain a separate local joined state. The application must suspend all prices whenever that state becomes invalid, even if cached rows still say open.
Merging the snapshot and queue
For each market/event, retain its last version. An object absent from the join snapshot counts as having the snapshot version. Apply a later message only when its version is greater than both the stored version and any remembered event-removal version.
Step
Received state
Action
1
Join snapshot version 102; market row version 100, probability 0.52.
Install snapshot; keep the gate closed during replay.
2
Buffered row version 103, probability 0.54.
Replace market row; newer than stored version 100.
3
Delayed row version 101, probability 0.53.
Ignore; stored version is already 103.
4
A different market absent from the snapshot arrives at version 99.
Ignore; absence is protected by join version 102.
5
Replay finishes, heartbeat checks still pass.
Complete join. Use prices through the freshness gate.
The small version numbers above are explanatory. Actual versions are larger integers. Do not use ts or arrival order to choose between two copies of an object.
When to suspend and rejoin
Condition
Immediate action
Recovery
No feed heartbeat for 3 seconds
Suspend all prices.
Keep the consumer; discard the old join buffer, wait for a fresh heartbeat and join again.
Session changes; sequence skips; or final:true
Suspend all prices.
Wait for a fresh non-final heartbeat, then join again.
Heartbeat received more than 3 seconds after sent_at
Suspend all prices.
Wait for a fresh heartbeat and join again. Check the client clock and backlog.
Connection/channel closes, or basic.cancel
Suspend all prices.
Reconnect with backoff, bind again and repeat the full join.
Binding selection changes
Suspend all prices.
Unbind obsolete keys, reconnect, bind new keys and rejoin.
Local join buffer overflows
Suspend all prices.
Discard it and restart from a fresh heartbeat; do not silently drop updates and remain joined.
Judge buffered heartbeat age using the time your code received it, not the time you later replay it. Check for missed deadlines after a stalled event loop. Rows still arriving do not replace the heartbeat requirement.
The 3-second silence threshold is not an end-to-end 3-second freshness guarantee. The protocol’s combined worst-case bound is about 8.6 seconds under its clock/timing assumptions. Inspect the latest heartbeat’s as_of to assess the publication watermark.
Events moving into or out of a selection
Moves are sent under both old and new routing keys. Determine current membership from the payload’s sport, competition ID and event ID. If a newer row/event no longer matches your bindings, remove that event and its rows only when the move is newer than every stored object for the event.
Remember the removal version until the next join. This prevents an old queued message from restoring a removed event or a delayed move from deleting newer snapshot data. Perform the version check before deciding to remove anything.
Standby and restart behavior
Each queue permits one active consumer. A standby process can be connected with no deliveries; it must remain suspended. On takeover, it waits for a fresh heartbeat and performs its own join. A reconnect or heartbeat alone does not reopen the price gate.
Keep each process’s result journal separate. Restarting the example reloads durable result versions so duplicate delivery does not apply the same settlement twice.
Outcome key from the market, such as over or home. Null when void.
void
True when the market is void.
revision
Starts at 1; increases on each correction.
resolved_at
UTC time the result was resolved.
A correction can replace a winner or void the market. The next example is an alternative later correction to the same market; it must arrive in a row with a newer version.
Validate the result message and identify market_id and version.
If the version is newer, commit the result and its version to durable storage together. Make any downstream settlement update idempotent.
Only after that commit succeeds, ACK the RabbitMQ delivery. A duplicate/older delivery can be acknowledged once a newer or identical result is already durably stored.
On a write failure, leave the delivery unacknowledged and stop or recover the consumer. Do not acknowledge malformed results as successfully stored.
Use a separate results channel with prefetch at most 1,000. Process results even while prices are suspended, and even when the result’s sport/event does not match your price bindings.
Reconcile missed results over REST
The result queue has a seven-day TTL and size bounds; it is not permanent storage. After a longer outage or suspected loss, list events updated since your last durable checkpoint, read all pages, then request each event’s detail to obtain market results. Do not add a scheduled/live-only status filter.
Replace that timestamp with your checkpoint. For a known market ID, use GET /v1/markets/{market_id}. REST market objects carry result revisions; AMQP rows carry row versions as well. Compare like identifiers rather than inventing a row version from a revision.
REST retention is also finite: final events may disappear after 7 days once all markets are settled, and ended/final events may disappear after 30 days. If a required event is no longer available, contact the operator; the API cannot provide an unlimited archive.
Settlement rules by sport
Sport
Rule
Football
1x2, totals and handicaps use regular time including stoppage time. Extra time and penalty shoot-outs do not count. A match abandoned before regular time ends voids every market.
Tennis
Moneyline is the match winner. Once play starts, retirement, default or disqualification awards the market to the advancing player. A match that never starts is void.
NBA
Full game including overtime. For an abandoned game, use the official result; without one, all markets are void.
NHL
Full game including overtime and shoot-out. For totals, a shoot-out adds one goal to the winner. For abandonment, use the official result; without one, all markets are void.
Postponement and resumption: the reference start is the scheduled start as it stood 24 hours before kickoff, or when first listed if later. A postponed match retains its markets if it starts within 48 hours of that reference; otherwise it is cancelled and void. An interrupted match must resume and finish within the same 48-hour window, or all markets are void.
Do not settle a market yourself from the live score. Wait for the published result; an event can be ended before every market is resolved.
Unknown, repeated or invalid parameter; invalid join body.
Correct the request; verify parameter names and filters.
401
Missing or invalid API key.
Check the bearer header.
403
Disabled/expired key, forbidden address, or AMQP not enabled for joins.
Confirm onboarding details with the operator.
404
Object not found or no longer retained.
Check the ID and retention window.
429
Rate limit reached.
Wait the number of seconds in Retry-After.
503
Temporarily unavailable or too busy.
Honor Retry-After; keep prices suspended during an unsuccessful join.
Other 5xx / timeout
Transient request or service failure.
Retry with jittered backoff; for joins use the 30-second request and 60-second overall limits.
error.request_id matches the X-Request-Id response header. Keyed REST requests provide RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds). Joins have a separate budget.
RabbitMQ refusals and closes
Symptom
Action
TLS verification failure
Use the supplied CA for the correct endpoint; confirm the server address and local clock.
Login 403 ACCESS_REFUSED
Check the assigned broker username/password, not the API key.
Vhost 530 NOT_ALLOWED
Confirm vhost feed and account access with the operator.
Channel 403
Check queue/exchange names and permitted operations. Do not declare or purge queues.
Channel 404
Check the assigned queue set.
basic.cancel
Reconnect, bind and rejoin.
Open connection, no deliveries
Possible standby consumer. Keep all prices suspended.
320 CONNECTION_FORCED
If account disabled, contact the operator. If too many unacknowledged messages, fix ACK handling. For restart/maintenance, reconnect and rejoin with backoff.
Delivery and request limits
Limit
Value / scope
Catalogue page
Default 100; maximum 500; cursor valid 10 minutes.
Large price response
Over 2,000 rows; one per key per 10 seconds by default.
AMQP joins
2 answers per key per 10 seconds, separate from REST polling.
Join filters
At most 4 filters; 500 sport/competition/event IDs total.
Broker account
4 connections and 8 channels per user.
Binding count
At most 500 per client queue.
Result prefetch
1–1,000 on the results channel.
Price queue
5,000 messages or 32 MiB; 10-second TTL.
Result queue
25,000 messages or 32 MiB; 7-day TTL.
AMQP body
At most 64 KiB.
Configured price publication cap
1,000 price updates/second service-wide; not a per-client allowance.
Queue overflow drops oldest messages. Recovery depends on heartbeat checks and a fresh snapshot; durable results still require your own storage. Queue and publication values above describe the configured limits. Account limits may be changed by the operator.
Empty or suspended data
Empty catalogue/join: a filter can validly match nothing. Check current /v1/sports and /v1/competitions, then inspect your binding IDs. Repeated joins: check clock synchronization, slow processing, missed heartbeat sequences and duplicate consumers. Journal startup failure: preserve the file and reconcile it; do not delete it to force startup.
Run the disposable broker scenarios before testing an assigned production connection. The local harness uses synthetic data and its own credentials.
Local broker scenarios
BASH
# Run from the extracted sport-feed-examples directory on Linux with Docker.
bash examples/amqp/test.sh python
bash examples/amqp/test.sh java
# Or run both:
bash examples/amqp/test.sh all
The harness creates and removes its own RabbitMQ container with no published ports. Allow about 1 GiB for the image/tools and several minutes per language. It does not use your production account.
Scenario
What it checks
Join
Snapshot + updates, result persistence and corrections.
Feed restart / crash
Immediate planned-stop suspension and heartbeat timeout after a crash.
Broker restart
Reconnect, rejoin and persistent result recovery.
Feed reconnection
New session invalidates the old view.
Resync / missing heartbeat
Session and sequence-gap recovery.
Event moves
Remove out-of-view events and add newly matching events.
Delayed move
Old messages cannot erase newer state or restore removed events.
Standby takeover
Standby remains suspended, then joins when active.
Slow consumer
Suspend after stale delivery; recover with a new join.
Checks for your application
Checkboxes are local review notes; they are not evidence of an executed test.
Run restart, duplicate delivery and write-failure tests against the disposable broker. On the assigned connection, verify credentials, catalogue access, a normal join and expected updates.
Contract revisions are recorded in the downloaded schemas. The HTTP path version is /v1.
Example payloads
Every JSON block has a download button. The files contain fictional data suitable for inspecting shapes and writing parser fixtures; they are not recordings of a live feed. Requesting the sample IDs will not find a real fixture.
The Python and JavaScript client libraries use WebSocket subscriptions, not RabbitMQ queues. Their snapshot and sequence rules are specified separately; do not apply the AMQP join procedure to a WebSocket connection.