The endpoints
Each one below lists its plan tier, every parameter, the response shape and an example. They
are the same entries as in the full reference, which
carries all 48 operations of the API on one page.
POST /webhooks
Register an outbound webhook (ULTRA, direct keys only)
Plan required: ULTRA · operationId: createWebhook
We POST the same frames the WebSocket sends to your HTTPS endpoint on every live score commit. Up to 3 webhooks per key (409 webhook_limit past that). The response is the ONLY time the signing secret is shown — store it.
Each delivery carries X-LTAPI-Signature (sha256=<hex> — HMAC-SHA256 of the RAW request body with your webhook secret; verify with a constant-time compare), X-LTAPI-Timestamp (Unix seconds at send time — reject stale replays at your edge) and X-LTAPI-Event (the frame type: score, break_point, break_point_result or point).
Delivery is best-effort, at-most-once, no replay: one attempt per frame with a ~3s timeout and redirects disabled. Every score frame is the complete current score, so a missed delivery self-corrects on the next commit. A point frame is an EVENT, not a state — a missed one does NOT self-correct; recover it with GET /matches/{matchId}/points?after_seq= and dedup by seq. After 25 consecutive failures the webhook is disabled automatically (enabled:false, last_error set — visible in GET /webhooks); delete and re-register to resume.
Responses
Example
curl https://api.livetennisapi.com/api/public/v1/webhooks \
-H "Authorization: Bearer twjp_..."
GET /webhooks
List your webhooks (ULTRA, direct keys only; never includes the secret)
Plan required: ULTRA · operationId: listWebhooks
Responses
Response fields
Example
curl https://api.livetennisapi.com/api/public/v1/webhooks \
-H "Authorization: Bearer twjp_..."
DELETE /webhooks/{webhookId}
Remove one of your webhooks (ULTRA, direct keys only)
Plan required: ULTRA · operationId: deleteWebhook
Parameters
Responses
Response fields
Example
curl https://api.livetennisapi.com/api/public/v1/webhooks/{webhookId} \
-H "Authorization: Bearer twjp_..."
GET /ws-token
Mint a connection token for the high-fan-out push feed (ULTRA)
Plan required: ULTRA · operationId: createWsToken
Returns a short-lived signed token plus the push WebSocket URL and the channel vocabulary: match:{match_id} per-match streams and slate:all for every live score frame. Frames are the same allowlist score objects the polling endpoints return. This is a separate surface from the native /ws feed described above — same ULTRA gate, built for high fan-out (no shared connection ceiling), and the recommended home for continuous/production streaming.
The endpoint speaks the **Centrifugo client protocol** (v2, JSON). Easiest path: the official Python (livetennisapi ≥ 1.4.0) and JS (≥ 1.5.0) SDKs ship a built-in PushStream client — no extra dependency. Raw protocol, if you prefer your own client: (1) open a WebSocket to ws_url; (2) send {"connect": {"token": "<token>"}, "id": 1} — the token goes INSIDE this JSON frame, never as a raw first message; (3) subscribe per channel with {"subscribe": {"channel": "slate:all"}, "id": 2}; (4) publications arrive as {"push": {"channel": ..., "pub": {"data": <frame>}}}; (5) the server's heartbeat is an empty JSON object {} — reply with {} promptly or you will be disconnected. Messages may batch several newline-delimited JSON objects. Tokens are short-lived and the connection closes around token expiry: mint a fresh token on EVERY reconnect and re-subscribe.
The channels object lists only channels that will actually deliver for your key right now (a channel name in this response is a promise). Where enabled server-side, additional channel families appear: point:match:{match_id} / point:slate (per-point events), listed — as point_match / point_slate in the vocabulary — only for keys whose plan carries the point surface, and signal:match:{match_id} / signal:slate (derived break_point, break_point_result and divergence events, and since 2026-09-12 the stoppage family: medical_timeout_start/end, trainer_called/_end, toilet_break_start/end, stoppage_start/end, pause_start/end). A family absent from the response will not deliver for your key right now. Deliberately separate channels: a slate:all subscriber asked for score states and never starts receiving events unasked. Point and signal frames are events, not states — a missed point does NOT self-correct on the next frame; recover it via GET /matches/{matchId}/points?after_seq= and dedup by seq.
WITHDRAWN STATES, AND HOW TO GET ONE BACK. This feed publishes every state we accept and runs no trust deference, while GET /matches/{matchId}/score runs the legality gate and can serve an older state than the newest row. So a state you receive here can later be one we do not stand behind, and when that happens a score_withdrawn frame follows the state's own score frame on the same channel. Eight keys, and no replacement score:
``json { "type": "score_withdrawn", "match_id": 4711, "kind": "withdrawn",
"superseded_sequence": 118, "reason": "illegal_completed_set",
"safe_to_resume": true, "ts": "2026-09-25T10:00:03.108Z",
"published_at": "2026-09-25T10:00:03.115Z" }
`
superseded_sequence is the sequence you hold and are to discard. The state to resume from is the one on the next score frame, or on a fresh GET /matches/{matchId}/score. kind is withdrawn (discard), deferred (the refused state may be early and is pending the next accept, so hold it) or withheld (nothing publishable, games is null). ts is when the judgement was made, published_at when this feed was handed the frame. A given (match, sequence) is withdrawn at most once per publishing process and there is no replay.
A consumer that was disconnected recovers the judgement from the durable record, added 2026-09-25: GET /matches?withdrawn_since=<instant> for the matches that had one, GET /matches/{matchId}/withdrawals for the whole record on a match (each row carrying the frame exactly as it went out, and the sequence served in the refused state's place), and GET /matches/{matchId}/score?sequence=N to ask about the one sequence you hold. Records are kept 90 days. The live verdict` on the score read is the same judgement on demand, but it is recomputed over the newest state, so it stops answering for N as soon as the next state is accepted. The record does not.
Responses
Response fields
Example
curl https://api.livetennisapi.com/api/public/v1/ws-token \
-H "Authorization: Bearer twjp_..."