Callbacks reference
Every webhook payload, field by field. All callbacks POST to your webhook, carry a deterministic eventId, and retry until acknowledged — semantics in the Callbacks & reliability chapter.
Common fields & retry tiers
| Field | Type | Description |
|---|---|---|
eventId | string | unique, deterministic id — dedupe on it (e.g. match_aborted:<matchId>) |
type | string | match_found · match_aborted · game_result · tournament_complete · tournament_round_started · fairplay_flag |
| Tier | Behaviour |
|---|---|
game_result · tournament_complete | retry forever (die only on a permanent 4xx ≠ 409) |
all other events | 20 attempts, then dead-letter (alerted on the engine side, never silent) |
backoff | [1, 2, 5, 15, 30, 60] seconds, last value repeats |
delivered | 2xx or 409 (idempotent duplicate) |
match_found
| Field | Type | Description |
|---|---|---|
matchId | string | match id (== the socket room id) |
variant / timeControl | string | game type |
tournamentId | string | null | set when the match comes from a tournament |
timeControlDetail | object | { base, inc } in seconds |
players[] | array | { userId, username, color } per player — the engine pushes the game to both sockets itself |
jsonc
{
"eventId": "evt_001",
"type": "match_found",
"matchId": "match_abc",
"variant": "standard",
"timeControl": "blitz_3_2",
"tournamentId": null,
"timeControlDetail": { "base": 180, "inc": 2 },
"players": [
{ "userId": "user_123", "username": "magnus_fan", "color": "white" },
{ "userId": "user_456", "username": "the_turk", "color": "black" }
]
}
// A notification, not a mechanism: the engine already pushed the game start to
// both sockets. Use it for bookkeeping — or to notify an OFFLINE tournament
// entrant whose round just opened (the one case it does real work).
// Direct play_bot games fire NO match_found callback; the autoBot fallback
// fires one with a single (human) player.
// Every seat carries username (omitted for a bot seat / unknown) — uniform with
// the WS seats and the other callbacks. You may ignore it; you own the canonical name.game_result
| Field | Type | Description |
|---|---|---|
matchId | string | match id |
tournamentId | string | null | set when from a tournament |
variant | string | rating category — file the rating from this, never from a remembered match_found |
timeControl | string | time-control id (e.g. blitz_3_2); EMPTY for a custom control |
speed | string | speed bucket the rating is keyed on (e.g. blitz); EMPTY for a custom control — record the game, do not rate it |
result | string | white_win | black_win | draw |
termination | string | checkmate | resign | timeout | stalemate | insufficient | threefold | fivefold | fifty_move | seventy_five | draw_agreement |
startedAt / endedAt | string (Y-m-d H:i:s.v) | game time span |
movesUci[] | string[] | full move list in UCI |
players[].userId / username / color | string | seat (username omitted for a bot seat / unknown) |
players[].score | number | 1 · 0.5 · 0 |
players[].ratingBefore | object | echoes the triple you supplied (may include lastGameAt / floor). OMITTED for unrated games |
players[].ratingAfter | object | computed { r, rd, vol } — persist this. OMITTED for unrated games |
players[].delta | number | ratingAfter.r − ratingBefore.r. OMITTED for unrated games |
jsonc
{
"eventId": "evt_003",
"type": "game_result",
"matchId": "match_abc",
"tournamentId": null,
"variant": "standard", "timeControl": "blitz_3_2", "speed": "blitz",
"result": "white_win",
"termination": "checkmate",
"startedAt": "2026-07-08 09:01:00.000",
"endedAt": "2026-07-08 09:07:32.000",
"movesUci": ["e2e4","e7e5","g1f3"],
"players": [
{ "userId": "user_123", "username": "magnus_fan", "color": "white", "score": 1,
"ratingBefore": { "r": 1512.3, "rd": 84.1, "vol": 0.0598, "lastGameAt": 1719828000000, "floor": 1400 },
"ratingAfter": { "r": 1524.7, "rd": 78.2, "vol": 0.0597 }, "delta": 12.4 },
{ "userId": "user_456", "username": "the_turk", "color": "black", "score": 0,
"ratingBefore": { "r": 1600.0, "rd": 60.0, "vol": 0.0600 },
"ratingAfter": { "r": 1589.1, "rd": 58.4, "vol": 0.0601 }, "delta": -10.9 }
]
}
// Unrated (rated:false or any bot game) → identical payload; ONLY the
// ratingBefore/After/delta block is omitted:
{
"eventId": "evt_010",
"type": "game_result",
"matchId": "match_790",
"tournamentId": null,
"variant": "standard", "timeControl": "blitz_3_2", "speed": "blitz",
"result": "draw",
"termination": "stalemate",
"startedAt": "2026-07-08 10:15:00.000",
"endedAt": "2026-07-08 10:21:44.000",
"movesUci": ["e2e4","e7e5","g1f3"],
"players": [
{ "userId": "user_123", "username": "magnus_fan", "color": "white", "score": 0.5 },
{ "userId": "user_456", "username": "the_turk", "color": "black", "score": 0.5 }
]
}match_aborted
| Field | Type | Description |
|---|---|---|
matchId | string | aborted match id |
tournamentId | string | null | set when from a tournament |
reason | string | no_show | aborted — no_show means the gate was never confirmed, not a failure to connect |
players[] | array | { userId, username, color } — the game never started, so there is no result and no rating change |
jsonc
{
"eventId": "match_aborted:match_abc",
"type": "match_aborted",
"matchId": "match_abc",
"tournamentId": null,
"reason": "no_show",
"players": [
{ "userId": "user_123", "username": "magnus_fan", "color": "white" },
{ "userId": "user_456", "username": "the_turk", "color": "black" }
]
}
// Engine follow-up (already done by the time you receive this): matchmaking →
// the player who showed up is requeued; tournament → the absentee forfeits.tournament_complete
| Field | Type | Description |
|---|---|---|
tournamentId | string | tournament id |
standings[] | array | { userId, rank, score, games } — final standings |
json
{
"eventId": "evt_004",
"type": "tournament_complete",
"tournamentId": "trn_44",
"standings": [
{ "userId": "user_456", "rank": 1, "score": 21, "games": 14 },
{ "userId": "user_123", "rank": 2, "score": 19, "games": 14 }
]
}tournament_round_started
jsonc
{ "eventId": "evt_005", "type": "tournament_round_started", "tournamentId": "trn_44", "round": 3 }
// Optional UI hint — safe to ignore.fairplay_flag
| Field | Type | Description |
|---|---|---|
flagType | string | repeat_pairing | sandbag_pattern | lopsided_pair | engine_assist |
userIds[] | string[] | flagged users |
matchIds[] | string[] | matches the evidence covers |
evidence | object | free-form, heuristic-specific detail |
jsonc
{
"eventId": "fairplay_flag:engine_assist:match_abc",
"type": "fairplay_flag",
"flagType": "engine_assist",
"userIds": ["user_123"],
"matchIds": ["match_abc"],
"evidence": { "engineMatchRate": 0.97, "pliesAnalyzed": 58 }
}
// The engine only signals; the platform correlates identity and enforces.