---
title: "The durable record of every score state we withdrew on this match (ULTRA)"
method: GET
path: "/matches/{matchId}/withdrawals"
---

# The durable record of every score state we withdrew on this match (ULTRA)

`GET /matches/{matchId}/withdrawals`

## Request

- Base URL: `https://api.livetennisapi.com/api/public/v1`
- URL: `https://api.livetennisapi.com/api/public/v1/matches/{matchId}/withdrawals`
- Auth: one of:
  - HTTP bearer
  - API key in header `X-API-Key`

Added 2026-09-25. One row per refused `sequence`, oldest first, each carrying the `score_withdrawn` frame exactly as the push feed sent it.

This is the RECOVERY PATH for a missed frame. The `verdict` object on `GET /matches/{matchId}/score` is recomputed on every read over the newest state, so it returns null again as soon as the next state is accepted, which in a live match is seconds. The record here is written when the judgement is made, before the frame is delivered, so a consumer that was disconnected can still learn that a sequence it holds was taken back. `GET /matches?withdrawn_since=` says which matches to ask about; `?sequence=N` on the score read answers for one sequence without fetching the list.

ULTRA, the tier that receives the frame on the push feed and the native WebSocket. An empty `data` is the ordinary answer and means nothing was withdrawn on that match. Records are kept 90 days from the withdrawal.

## Path parameters

- `matchId` integer, required

## Response `200`

Withdrawals on this match, oldest first

- object
  - `match_id` integer
  - `data` ScoreWithdrawal[]
    - `sequence` integer: The refused state's `sequence` — the value you hold from the feed, and the frame's `superseded_sequence`.
    - `kind` 'withdrawn' | 'deferred' | 'withheld': The frame's `kind`. Only `withdrawn` means discard and resume.
    - `reason` string: The legality gate's slug, the same value the frame carries.
    - `published_at` string, date-time, nullable: When the refused state's OWN `score` frame was handed to the push feed. Null when that state never went out on the feed — the judgement was still made and the REST read served it.
    - `withdrawn_at` string, date-time: When the judgement was made. The frame's `ts`.
    - `replaced_by_sequence` integer, nullable: The `sequence` of the state served in the refused one's place, the newest legal state at that moment. Null on `withheld`, where nothing within the scan was publishable. It is on the record and NOT on the frame.
    - `frame` object: The `score_withdrawn` frame exactly as the push feed, the native WebSocket and webhooks sent it, stamped once so the stored copy is the wire copy. Its keys are `type`, `match_id`, `kind`, `superseded_sequence`, `reason`, `safe_to_resume`, `ts` and `published_at`. It carries NO replacement score and no new sequence: the state to resume from is the one on the next `score` frame, or on a fresh `GET /matches/{matchId}/score`.

## Other responses

- `401`: Missing, unknown, or disabled credentials
- `403`: Your tier doesn't unlock this endpoint
- `404`: No such resource, or no data yet
- `410`: The match id existed and was merged into another match record (since 2026-09-05). The body is `MatchMerged`, not `Error`: it carries a forwarding address, which is the whole point of answering 410 rather than 404.
- `429`: Rate limit exceeded (Retry-After header present). Three body shapes, told apart by `error` and `scope`: the per-MINUTE limit (`rate_limited`, with `upgrade_url`, `tier` and `price` naming the next tier up); the per-DAY quota (`rate_limited` with `scope: "day"`, `limit_per_day`, and `resets_at` — the absolute ISO instant the daily window resets); and `abuse_throttled` with `retry_at_epoch` — a 24-hour block applied to clients that keep hammering far past their cap, which a well-behaved retry loop never sees. Fix the loop rather than retrying through it.

---

[API](https://skmtc.dev/livetennisapi/apis/live-tennis-api.md) · [All operations](https://skmtc.dev/livetennisapi/apis/live-tennis-api/llms.txt) · [OpenAPI document](https://skmtc.dev/livetennisapi/apis/live-tennis-api/revisions/7881947e8e43?raw)
