Skip to main content
POST
Initiate UOF-style stateful recovery

When to call it

Use this endpoint whenever your AMQP consumer falls behind and you need to resync without polling REST for every match individually. Typical triggers:
  • AMQP connection dropped and your durable queue’s TTL elapsed.
  • Service restart with no persisted last-message timestamp.
  • The <alive> message arrived with subscribed="0" (producer downtime) — UOF convention is to immediately initiate recovery.
Before starting monitoring or recovery, fetch GET /v1/descriptions/producers. Recovery runs per discovered producer; the catalog’s producer.id matches the product attribute in feed messages, and its scope can be used as the UOF-compatible recovery path alias.

How it works

You make a single REST call. The server schedules a background replay onto your tenant’s AMQP vhost (the same exchange the live feed uses, ttfeed). For every booked match the replay publishes:
  1. One <odds_change> listing the current active B2B market offers with up-to-date odds.
  2. One <bet_settlement> per match for offers settled between after and now.
  3. One <bet_cancel> per match for offers voided between after and now.
  4. A terminating <snapshot_complete request_id="…"> so you know the replay is finished.
Every recovery XML envelope carries the same client-provided request_id as a top-level attribute. Classify replay messages by that positive integer attribute; the recovery=1 AMQP header remains available but is not required for correlation. Recovery routing keys use priority lo (vs live hi) and echo node_id in their final section.

Required scope

  • Minimum: cs2:recovery:initiate
This scope is included by default in the cs2.odds.basic bundle, so existing odds-feed customers don’t need a new key.

Parameters

Full snapshot versus incremental recovery

  • Full snapshot: omit after or send after=0. Ticktock republishes every current active market for booked matches, followed by <snapshot_complete>. It does not query historical settlements/cancels and does not apply the stateful recovery window.
  • Incremental recovery: send a positive Unix-millisecond after. Current active markets are republished together with settlement/cancel transitions after that timestamp.
The incremental window defaults to 4320 minutes (3 days) and is advertised by GET /v1/descriptions/producers. It is configurable per deployment, not a hard-coded global maximum. A positive after older than the configured window or in the future returns HTTP 422.

Errors

Concurrency

The in-flight lock is keyed on (tenant, node_id). That means:
  • Different node_id values from the same tenant can recover at the same time. For example, nodes 3001 and 3002 can run independent replays and bind queues to their respective numeric routing-key suffixes.
  • Same node_id (including omitting it — empty maps to the implicit slot "global") is serialized. A second request before the first one’s <snapshot_complete> arrives gets HTTP 409. Wait for that envelope, then retry if you still need to.
The lock has a server-side TTL (10 minutes) that acts as the safety net if a replay worker crashes before releasing it, so a hung recovery cannot lock you out indefinitely.

Example

Client recipe

1

Detect the loss

Your AMQP consumer disconnects, falls behind, or you see an <alive subscribed="0"> message. Stop processing live messages until recovery completes — either pause your consumer or buffer the incoming envelopes and apply them after <snapshot_complete>.
2

Initiate the replay

POST /cs2/v1/recovery/initiate_request with after = <unix_ms of last processed message>. The server returns 202 Accepted immediately; the replay is scheduled in the background.
3

Apply recovery envelopes

Resume reading from your AMQP queue. Treat an envelope as recovery when its root XML request_id equals the positive integer you sent. The recovery=1 header and priority lo remain supplementary metadata.
4

Switch back to live

When you receive <snapshot_complete request_id="..."/> whose request_id matches the one you sent, your state is in sync. Apply any buffered live messages, then resume normal processing.
If you get a 409 “already running” for your node_id, don’t retry — just wait for the in-flight <snapshot_complete> to arrive; a single recovery per node delivers everything you need. If you got the 409 from a different node sharing your tenant, that’s expected and harmless — your own node_id’s slot is still free, just send your request with your tag.

Routing keys produced

When node_id is omitted, the last section is -.

See also

Authorizations

X-API-Key
string
header
required

Tenant API key issued during onboarding

Query Parameters

after
integer | null

Unix timestamp in milliseconds for incremental recovery. Settlements / voids strictly after this moment are replayed. Omit or pass 0 for a full active-market snapshot without historical settlement/cancel replay.

Required range: x >= 0
request_id
string
required

Client-chosen positive integer echoed verbatim in the root request_id attribute of every recovery XML envelope.

Maximum string length: 64
Pattern: ^[1-9][0-9]*$
node_id
string | null

Optional positive integer client-node identifier. When supplied, it is echoed verbatim in the last section of every recovery AMQP routing key.

Maximum string length: 64
Pattern: ^[1-9][0-9]*$

Response

Successful Response

The response is of type Response Initiate Recovery Cs2 V1 Recovery Initiate Request Post · object.

Last modified on June 21, 2026