Skip to main content

Error format

Every error is {"error": "<code>"} — one snake_case code, no message or details object. Two codes carry a parameter after a colon: invalid_startsAt:<the bad value> and unknown_event:<the bad name>. The API uses exactly four error statuses: 400, 401, 404, 409. It never returns 403. Unexpected server faults surface as plain 500s.

Classification and retry rules

Idempotency is your retry safety net

Exactly three writes are idempotent upserts on (source, externalId) and safe to repeat: POST /leagues, POST /leagues/{leagueId}/games, POST /leagues/{leagueId}/officials. Always send a stable externalId from an integration — it is what makes a timeout recoverable by simple re-send. Every other write is not idempotent — never auto-retry after an ambiguous failure (timeout, dropped connection) without checking state first:
  • POST /games/{gameId}/assign — its externalId/source fields identify the official; they do not make the assignment idempotent. Re-fetch the game to see whether the claim landed before re-sending.
  • POST /webhooks — registering twice creates two endpoints. GET /webhooks first.
  • Any DELETE.

Rules for AI agents

If you are an agent operating this API (via REST or the MCP tools), follow these rules:
1

Reads are free; writes need intent

List/get calls are always safe. Only call write tools when the user’s request actually requires the change.
2

Summarize and confirm before destructive actions

Before any of the following, present a plain-language summary of exactly what will change (which league/game/official, by name and id) and get explicit confirmation:
  • Cancel a game (DELETE /games/{gameId} / whistle_cancel_game) — cancels open slots.
  • Remove an official (DELETE /leagues/{leagueId}/officials/{userId} / whistle_remove_official) — also reopens every position they’d claimed.
  • Unassign a position (DELETE /games/{gameId}/assign / whistle_unassign_position) — deletes the claim.
  • Delete a webhook (DELETE /webhooks/{webhookId} / whistle_delete_webhook) — removes delivery history; endpoint may belong to another integration.
  • Bulk mutations — any loop that writes to many records (mass game updates, batch official changes). Confirm the batch as a whole, with a count.
3

Never auto-repeat writes without stable identifiers

Repeating a write is safe only when it’s an upsert keyed on (source, externalId). If you don’t have a stable externalId for a record, a repeated POST creates a duplicate — stop and verify state instead of retrying.
4

Treat 409s as information, not failure

A conflict means the world changed (someone claimed the slot, the official was already removed). Re-fetch, report the actual state, and re-plan rather than forcing the original action.
5

Respect the scope boundary

Your key sees only leagues and webhook endpoints in your source namespace (plus explicit grants) — out-of-scope resources answer 404 exactly like nonexistent ones. Don’t interpret a 404 as “gone” and re-create records on that basis alone; re-list what your key can see and reconcile from there.
Never place a real API key in a tool config file, chat message, or code sample. Connect via environment variables as shown in the MCP quickstart.
Last modified on July 13, 2026