BeatKhana! DevelopmentDeveloper guides ↗

Socket.IO API

Realtime BeatKhana: match coordination, overlays, live co-editing, custom overlay packets and donations. Use REST (Swagger · Scalar) for durable reads and changes; use these sockets for anything that has to update live.

Productionwss://wss.beatkhana.com
Stagingwss://wss.staging.beatkhana.com
Localws://localhost:48522
ProtocolSocket.IO 4 (WebSocket transport)
PayloadsMessagePack in, MessagePack events out
Authauthenticate packet with a bearer token

Quick start

The official TypeScript client (bk-client) handles encoding, acknowledgements and reconnects:

bk-client
import { BKClient } from "bk-client";

const bk = new BKClient({ wsUrl: "https://wss.beatkhana.com" }).setAuthToken(accessToken);
bk.socket.connect({ authenticate: true });

// Follow a match (works signed out too) and react to every change.
const stop = bk.socket.matches.observe({ matchGuid }, {
  // Full state, newest revision only; update carries the notice ("scoresConfirmed", "replay", ...).
  onDetail: (detail, update) => { render(detail); if (update?.notice) toast(update.notice.kind); },
  onRealtime: (frame) => drawLiveScores(frame), // staff and overlays only
});

Any Socket.IO 4 client works. This is the whole protocol with socket.io-client and @msgpack/msgpack:

socket.io-client
import { io } from "socket.io-client";
import { encode, decode } from "@msgpack/msgpack";

const socket = io("https://wss.beatkhana.com", { transports: ["websocket"] });
const send = (event, payload = {}) => socket.timeout(15000).emitWithAck(event, encode(payload));
const on = (event, handler) => socket.on(event, (bytes) => handler(decode(bytes)));

socket.on("connect", async () => {
  // 1. Authenticate on every (re)connect. Note the "Bearer " prefix.
  const auth = await send("authenticate", { token: `Bearer ${accessToken}` });
  if (!auth.success) throw new Error(auth.error);

  // 2. Subscribe; the acknowledgement contains the current state.
  const sub = await send("match:subscribe", { matchGuid });
  if (sub.type !== "success") throw new Error(sub.error);
  render(sub.detail);
});

// 3. Apply live updates, dropping anything older than what you have.
on("match:update", ({ detail }) => {
  if (detail.revision > current.revision) render(detail);
});

How packets work

  1. Commands are emits with an acknowledgement. Emit the packet name with one payload and a callback (emitWithAck). The server answers through the callback exactly once.
  2. Encode the payload with MessagePack. Send encode(payload). Plain objects are accepted too, but MessagePack keeps binary-safe parity with the official clients.
  3. Read the acknowledgement as a plain object. Success has type: "success" plus packet-specific fields; failure is { type: "fail", error }. The only exception is authenticate, which answers { success, userData | error }.
  4. Server events are MessagePack bytes. Decode every server event before use, except connected (plain JSON).
  5. Without a callback, the server answers with a separate <packet>:response event (MessagePack). This exists for old clients; new code should always pass a callback.

Authentication and permissions

Authentication belongs to the connection. Send authenticate with { token: "Bearer <access token>" } after every connect and reconnect. Tokens are the same BeatKhana JWTs the REST API accepts (website sessions or OAuth applications).

BadgeMeaning
handshakeThe authenticate packet itself.
publicAnyone, signed in or not (match:subscribe, match:unsubscribe). Signed-in staff get the private view.
ws:readAuthenticated socket whose token has ws:read (subscriptions and reads).
ws:writeAuthenticated socket whose token has ws:write (every command). ws:write implies ws:read.

Scopes only say what the application may do. The user's global roles, tournament roles and match assignments still decide each action; every card lists Who may send it. A missing scope fails with { type: "fail", error, requiredScope }.

Rooms and subscriptions

Subscribing joins Socket.IO rooms; server events are sent to rooms. You never join rooms directly. Room membership is lost on disconnect, so subscribe again after reconnecting.

RoomJoined byReceives
match:<matchGuid>
Full workflow including pending scores and realtime frames.
match:subscribe with staff access; viewing an overlay that shows the matchmatch:update, match:realtime, match:settings
match:<matchGuid>:public
Public view only.
match:subscribe without staff access (signed out allowed)match:update, match:settings
casters:<matchGuid>casters:subscribe (signed out allowed)casters:update
<tournamentGuid>:<overlayGuid>getTournamentOverlay, subscribeTournamentOverlayDocuments, getCustomOverlayStatestournamentOverlayUpdate:match, tournamentOverlayDocuments:update, customOverlayStateUpdate, customOverlayStatesChanged, customOverlayPacket, customOverlayPacketsChanged, <packetType>
<overlayGuid>
Legacy room name; events are de-duplicated for sockets in both overlay rooms.
getTournamentOverlay, getCustomOverlayStatestournamentOverlayUpdate:match, customOverlayStateUpdate, customOverlayPacket, <packetType>
collab:general:<tournamentGuid>collab:join scope tournament-generalcollab:operation, collab:participants, collab:saved, collab:state
collab:updates:<tournamentGuid>collab:join scope tournament-updatescollab:operation, collab:participants, collab:saved, collab:state
collab:match-notes:<matchGuid>collab:join scope match-notescollab:operation, collab:participants, collab:saved, collab:state
<tournamentGuid>:donationsdonation:subscribetournament:donation
awardsListenersubAwardsshowCategory
TAMock-<clientGuid>TAMock:SubClientLogs, TAMock:InstantiateTAMock:Instance:Data

Match coordination model

Coordination is match-scoped. Every match:* command takes { matchGuid, tournamentGuid? }, runs one change in a database transaction, and broadcasts the complete new state as match:update to both match rooms. The acknowledgement carries the same detail (same revision) plus your access.

  • Revisions. detail.revision grows with every change (and is larger after a server restart). Keep the newest; ignore older details that arrive late.
  • Panel lock. Coordinator and referee commands only work between the match's actual start (after 2020) and a later actual end. match:start / match:end set those times. Setup commands (flow, TA tournament, Discord role, picks and bans visibility for hosts) work before the start.
  • Access. Coordinators and referees act on matches they are assigned to, or on any match when they hold the role and nobody is assigned. Head coordinators, organisers, admins and staff can do everything. The match settings can give coordinators referee powers, score entry, match management and stream notifications.

cancelAwaitingScores returns to players_selection; warmups (setWarmupMode) never save scores. Realtime frames are display-only and never become results by themselves.

Errors and reconnecting

  • Show error to users: messages are written for people (e.g. Complete warmups before assigning Side 1.).
  • An acknowledgement that never arrives means the connection dropped; Socket.IO's timeout() rejects the promise. Do not blindly resend commands that are not idempotent (submitting scores, picks and bans).
  • After a reconnect: authenticate, subscribe again, and treat the snapshot in the acknowledgement as the truth. Missed events are not replayed, except collaboration operations (collab:join with resume).
  • The server keeps live state (realtime frames, collaboration sessions, room membership) in one process. Deployments behind a load balancer need sticky sessions.

Connection

Handshake and authentication.

Packets you send 2

emit

authenticate

handshake

Sign the socket in with a BeatKhana access token.

Send this first, and again after every reconnect: authentication belongs to the connection, not the user. Packets that need ws:read or ws:write fail until it succeeds. Signed-out sockets may still use match:subscribe (public view).

Unlike every other packet, the answer uses success instead of type.

Request payload

FieldTypeDescription
tokenrequiredstringBearer <access token>. The Bearer prefix is required.

Acknowledgement

FieldTypeDescription
successrequiredboolean
userDataobjectDecoded token: guid, discordId, username, avatarUrl, scopes.
errorstringWhen success is false: No token provided, Invalid token or Authentication error.

Failures: SocketFailure.

Example request
{
  "token": "Bearer eyJhbGciOiJSUzI1NiIs…"
}
Example acknowledgement
{
  "success": true,
  "userData": {
    "guid": "…",
    "discordId": "123456789012345678",
    "username": "luna",
    "scopes": [
      "ws:read",
      "ws:write",
      "rest:read"
    ]
  }
}
emit

subscribe

ws:read

Reserved generic subscription packet. It currently performs no subscription.

Answers Subscription type not specified! without subscriptionType, otherwise does nothing. Use the specific packets (match:subscribe, getTournamentOverlay, donation:subscribe...).

Request payload

FieldTypeDescription
subscriptionTypestring

Acknowledgement

{ type: "success" }

Failures: SocketFailure.

Events you receive 1

on

connected

plain JSON

Sent once right after connecting.

Plain JSON (not MessagePack). clientGuid identifies this connection in server logs; it changes on every reconnect.

Roomsthe connecting socket
WhenConnection established.

Payload

FieldTypeDescription
clientGuidrequireduuid
Example payload
{
  "clientGuid": "2f0c…"
}

Match coordination

Match rooms and every match:* command used by coordinators, referees and stream hosts.

Packets you send 27

emit

match:subscribe

public

Follow a match. Works signed in or out; the server picks the staff or the public room.

Coordinators, referees, stream hosts and staff of the match join match:<matchGuid> and receive the full workflow (pending scores, realtime frames). Everyone else joins match:<matchGuid>:public. Pass overlayGuid from an overlay that currently shows this match to receive the full detail as an overlay viewer.

The acknowledgement contains the current detail, so you do not need a separate REST call. Subscribe again after reconnecting.

WhoAnyone. Staff access needs an authenticated socket whose token has ws:read.
Joinsmatch:<matchGuid>match:<matchGuid>:public

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
overlayGuiduuidOptional: overlay showing this match.

Acknowledgement

FieldTypeDescription
typerequired"success"
roomrequiredstringThe room joined.
accessrequiredMatchAccess
detailrequiredMatchLiveDetail

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41"
}
Example acknowledgement
{
  "type": "success",
  "room": "match:4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41:public",
  "access": {
    "authenticated": false,
    "canViewPanel": false,
    "…": "…"
  },
  "detail": {
    "revision": 1791503530504000,
    "isPrivate": false,
    "…": "…"
  }
}
emit

match:unsubscribe

public

Stop following a match.

The socket stays in the staff room if an overlay it views still shows the match.

WhoAnyone.
Leavesmatch:<matchGuid>match:<matchGuid>:public

Request payload

FieldTypeDescription
matchGuidrequireduuid

Acknowledgement

FieldTypeDescription
typerequired"success"

Failures: SocketFailure.

emit

match:start

ws:write

Start the match: sets its actual start time and unlocks the coordinator and referee panels.

Refused when the match is already live. Starting a concluded match again moves the start past the old end and reopens the panels. Swiss brackets progress automatically.

WhoStream hosts, head coordinators, organisers, admins, staff, global staff (access.canStart).
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab"
}
Example acknowledgement
{
  "type": "success",
  "event": "start",
  "notice": {
    "kind": "matchStarted"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:end

ws:write

End the live match: sets its actual end time and locks the panels.

Clears the realtime frame and the TournamentAssistant room (currentRoomGuid); referee panels close that room in TournamentAssistant. Only a live match can be ended.

WhoReferees, stream hosts and staff; coordinators when allowCoordinatorManageMatch is on (access.canEnd).
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab"
}
Example acknowledgement
{
  "type": "success",
  "event": "end",
  "notice": {
    "kind": "matchEnded"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:giveDiscordRole

ws:write

Give the tournament's Discord Current Match Role to every captain, player and reserve of the match's teams.

Requires a configured Current Match Role (settings.discordMatchRole). The notice reports changed, unchanged, missing and failed players and is sent to the staff room only.

WhoReferees, stream hosts and above (access.canManageDiscordRole). Works before the match starts.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab"
}
Example acknowledgement
{
  "type": "success",
  "event": "giveDiscordRole",
  "notice": {
    "kind": "discordRoleGiven"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:removeDiscordRole

ws:write

Remove the Discord Current Match Role from the match's players.

WhoSame as match:giveDiscordRole.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab"
}
Example acknowledgement
{
  "type": "success",
  "event": "removeDiscordRole",
  "notice": {
    "kind": "discordRoleRemoved"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:setPlayersDeafened

ws:write

Server-deafen or undeafen the confirmed playing players in the tournament's Discord server.

Targets the confirmed players of teamGuid, or both players of a solo match. Players who are not in the server or not in a voice channel are reported in the notice, which is sent to the staff room only.

The tournament's Discord bot needs the Deafen Members permission.

WhoCoordinator. Only once the playing players are confirmed.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
deafenedrequiredbooleanTrue to deafen, false to undeafen.
teamGuiduuidThe coordinated team. Optional in a solo match.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "deafened": true,
  "teamGuid": "00000000-0000-4000-8000-000000000000"
}
Example acknowledgement
{
  "type": "success",
  "event": "setPlayersDeafened",
  "notice": {
    "kind": "playersDeafened"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:setSide1

ws:write

Assign the team on logical Side 1; picks and bans resolve s1*/s2* steps against it.

Accepted only after warmups are completed (and not active) and before the first pick or ban.

Refused while the match panel is locked (before the match starts or after it ends).

WhoReferee.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
teamGuidrequireduuidA team in this match.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "teamGuid": "00000000-0000-4000-8000-000000000000"
}
Example acknowledgement
{
  "type": "success",
  "event": "setSide1",
  "notice": {
    "kind": "sides"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:setPicksBansVisibility

ws:write

Show or hide picks and bans on overlays.

WhoStream hosts and staff at any time; referees while the match is live.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
visiblerequiredboolean

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "visible": true
}
Example acknowledgement
{
  "type": "success",
  "event": "setPicksBansVisibility",
  "notice": null,
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:setPlayingPlayers

ws:write

Select which players play the next map.

Refused while players are locked (cancel awaiting scores first). In solo matches every solo player plays and playerGuids is ignored. Moves the workflow to players_selection.

Refused while the match panel is locked (before the match starts or after it ends).

WhoCoordinator.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
playerGuidsrequireduuid[]Players on teams in this match.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "playerGuids": [
    "00000000-0000-4000-8000-000000000000"
  ]
}
Example acknowledgement
{
  "type": "success",
  "event": "setPlayingPlayers",
  "notice": null,
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:confirmPlayingPlayers

ws:write

Lock the selected players and start waiting for their scores.

Clears pending scores and the realtime frame, sets playersLocked and moves to awaiting_scores.

Refused while the match panel is locked (before the match starts or after it ends).

WhoReferee.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab"
}
Example acknowledgement
{
  "type": "success",
  "event": "confirmPlayingPlayers",
  "notice": {
    "kind": "playersConfirmed"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:cancelAwaitingScores

ws:write

Unlock the players and discard pending scores.

Back to players_selection.

Refused while the match panel is locked (before the match starts or after it ends).

WhoReferee.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab"
}
Example acknowledgement
{
  "type": "success",
  "event": "cancelAwaitingScores",
  "notice": null,
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:setWarmupMode

ws:write

Start or end a warmup.

Warmup scores are never saved. Ending a warmup marks warmups completed, which picks and bans require.

Refused while the match panel is locked (before the match starts or after it ends).

WhoReferee.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
activerequiredbooleanTrue starts a warmup, false ends it.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "active": true
}
Example acknowledgement
{
  "type": "success",
  "event": "setWarmupMode",
  "notice": {
    "kind": "warmupStarted"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:setTournamentAssistantRoom

ws:write

Record the TournamentAssistant room (TA match) the match is played in.

Refused while the match panel is locked (before the match starts or after it ends).

WhoReferee.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
roomGuidrequireduuid | nullTA match GUID, or null to clear.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "roomGuid": "00000000-0000-4000-8000-000000000000"
}
Example acknowledgement
{
  "type": "success",
  "event": "setTournamentAssistantRoom",
  "notice": null,
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:publishRealtimeScores

ws:write

Relay a realtime TournamentAssistant score frame to staff and overlays.

Never written to the database: the server keeps only the latest frame per match in memory and sends it as match:realtime to the staff room. The acknowledgement carries detail: null.

Send frames at the rate TournamentAssistant produces them; a new frame replaces the previous one.

Refused while the match panel is locked (before the match starts or after it ends).

WhoCoordinator.

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
realtimeScoresrequiredobjectFrame keyed by player (shape chosen by the client; bk-client uses its TA runtime projection).
playerPlatformIdsobjectOptional. BeatKhana user GUID -> numeric platform ID of the TA user in the match. Replaces the stored identities; omit to keep them.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "realtimeScores": {},
  "playerPlatformIds": {}
}
Example acknowledgement
{
  "type": "success",
  "event": "publishRealtimeScores",
  "notice": null,
  "detail": null,
  "access": {
    "canCoordinate": true,
    "…": "…"
  }
}
emit

match:savePendingScoreDrafts

ws:write

Save completed TournamentAssistant scores as drafts so they survive panel refreshes.

Upserts the latest completed score of each player without advancing the workflow. Requires locked players and the awaiting_scores or score_review phase; one score per confirmed playing player, on their correct team. Warmup scores are rejected.

Refused while the match panel is locked (before the match starts or after it ends).

WhoCoordinator.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
scoresrequiredMatchOperationsScore[]

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "scores": [
    "<MatchOperationsScore>"
  ]
}
Example acknowledgement
{
  "type": "success",
  "event": "savePendingScoreDrafts",
  "notice": null,
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:submitPendingScores

ws:write

Submit the scores of the current map.

Requires one automatic or manual score for every confirmed playing player. A referee submission is final: scores are saved and the map point goes to the single best team under the scoring rule. A coordinator submission moves the match to score_review until a referee sends match:reviewPendingScores.

Referees may call a replay by setting isCallingReplay: true on every score of exactly one team: the round is saved, that team loses one replay, no point is awarded and the workflow stays in awaiting_scores.

Refused while the match panel is locked (before the match starts or after it ends).

WhoReferees; coordinators when allowCoordinatorSetScore is on (access.canSubmitScores).
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
scoresrequiredMatchOperationsScore[]

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "scores": [
    "<MatchOperationsScore>"
  ]
}
Example acknowledgement
{
  "type": "success",
  "event": "submitPendingScores",
  "notice": {
    "kind": "scoresConfirmed"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:reviewPendingScores

ws:write

Referee decision on scores a coordinator submitted.

confirm saves and awards the point; replay saves the round as a replay for replayTeamGuid; discard drops the submission and returns to awaiting_scores. scoreAdjustments can mark individual scores as not counted first.

Refused while the match panel is locked (before the match starts or after it ends).

WhoReferee.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
decisionrequiredconfirm | replay | discard
replayTeamGuiduuid | nullRequired for replay.
scoreAdjustmentsobject[]
scoreAdjustments[].userGuidrequireduuid
scoreAdjustments[].isCountedrequiredboolean
scoreAdjustments[].notCountedReasonstring | null

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "decision": "confirm",
  "replayTeamGuid": "00000000-0000-4000-8000-000000000000",
  "scoreAdjustments": [
    {
      "userGuid": "00000000-0000-4000-8000-000000000000",
      "isCounted": true,
      "notCountedReason": "notCountedReason"
    }
  ]
}
Example acknowledgement
{
  "type": "success",
  "event": "reviewPendingScores",
  "notice": {
    "kind": "scoresConfirmed"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:reset

ws:write

Delete every pick, ban and score of the match and reset points and replays.

WhoStream hosts and staff at any time; referees while the match is live (access.canResetMatch).
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
confirmMatchNumberrequiredinteger | stringMust equal the match number, to make the reset deliberate.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "confirmMatchNumber": 1
}
Example acknowledgement
{
  "type": "success",
  "event": "reset",
  "notice": {
    "kind": "matchReset"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:saveMapScores

ws:write

Add, correct or remove scores on one map of the pool (the score editing on the match summary).

Existing rows are updated in place so replay rounds keep their order. Banned maps are refused. awardPoint gives the point to the best team among the newly added scores.

Refused while the match panel is locked (before the match starts or after it ends).

WhoReferees while the match is live; referees who can also start the match (hosts, head coordinators, staff) at any time.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
mapGuidrequireduuidA non-banned map in the match pool.
scoresrequiredMatchMapScoreEdit[]
deleteScoreGuidsuuid[]Scores on this map to remove.
awardPointboolean

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "mapGuid": "00000000-0000-4000-8000-000000000000",
  "scores": [
    "<MatchMapScoreEdit>"
  ],
  "deleteScoreGuids": [
    "00000000-0000-4000-8000-000000000000"
  ],
  "awardPoint": true
}
Example acknowledgement
{
  "type": "success",
  "event": "saveMapScores",
  "notice": {
    "kind": "scoresEdited"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:setDetails

ws:write

Set points and remaining replays of the first two teams.

Refused while the match panel is locked (before the match starts or after it ends).

WhoReferee.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
scoresobject
scores.side1Scoreinteger
scores.side2Scoreinteger
replaysobject
replays.side1ReplaysRemaininginteger
replays.side2ReplaysRemaininginteger

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "scores": {
    "side1Score": 1,
    "side2Score": 1
  },
  "replays": {
    "side1ReplaysRemaining": 1,
    "side2ReplaysRemaining": 1
  }
}
Example acknowledgement
{
  "type": "success",
  "event": "setDetails",
  "notice": {
    "kind": "pointsChanged"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:addPicksBansAction

ws:write

Fill the next step of the picks and bans flow.

Requires a map pool, completed warmups and a selected flow. The server validates the step against the flow (expected step number, acting side, map or flair availability, no map in progress) and serialises actions per match, so two panels cannot fill the same step.

With autoPostPicksBans on, the step is announced in the current match channel using the mapPick, mapBan, tiebreaker, categoryPick, categoryBan and sidesDecided message templates. Clearing maps is not announced.

Refused while the match panel is locked (before the match starts or after it ends).

WhoCoordinator.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
actionrequiredobject
action.flowOrderrequiredintegerThe flow step being filled; must be the next one.
action.mapGuiduuid | nullMap for map picks, bans and tiebreakers.
action.flairGuiduuid | nullCategory for flair picks and bans.
action.teamGuiduuid | nullFor decideTeams steps: the team that becomes Side 1.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "action": {
    "flowOrder": 1,
    "mapGuid": "00000000-0000-4000-8000-000000000000",
    "flairGuid": "00000000-0000-4000-8000-000000000000",
    "teamGuid": "00000000-0000-4000-8000-000000000000"
  }
}
Example acknowledgement
{
  "type": "success",
  "event": "addPicksBansAction",
  "notice": {
    "kind": "mapPick"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:removePicksBansAction

ws:write

Undo a picks and bans action and every later one (they depend on it).

Refused while the match panel is locked (before the match starts or after it ends).

WhoCoordinator.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
actionGuidrequireduuid

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "actionGuid": "00000000-0000-4000-8000-000000000000"
}
Example acknowledgement
{
  "type": "success",
  "event": "removePicksBansAction",
  "notice": {
    "kind": "actionUndone"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:resetPicksBans

ws:write

Remove every picks and bans action.

Refused while the match panel is locked (before the match starts or after it ends).

WhoReferee.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab"
}
Example acknowledgement
{
  "type": "success",
  "event": "resetPicksBans",
  "notice": {
    "kind": "picksBansReset"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:callTimeout

ws:write

Call a team timeout or a technical timeout.

A team timeout needs teamGuid. The match settings (timeouts) decide whether teams may call one before a picks and bans step, before the replay decision and before the next map; never while players are in a map (see MatchOperationsState.timeoutAvailability).

Coordinators are refused outside those times. Referees and stream hosts are refused too unless they send override: true.

Refused while another timeout is running. Posts in the current match channel when autoPostTimeouts is on.

Refused while the match panel is locked (before the match starts or after it ends).

WhoTechnical: referees, stream hosts and staff. Team: the same, and coordinators when allowCoordinatorTimeoutStart is on.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
kindrequiredteam | technical
teamGuiduuidRequired for a team timeout.
durationSecondsintegerDefaults to timeouts.defaultSeconds from the match settings.
overridebooleanReferees and stream hosts: call a team timeout outside the allowed times.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "kind": "team",
  "teamGuid": "00000000-0000-4000-8000-000000000000",
  "durationSeconds": 1,
  "override": true
}
Example acknowledgement
{
  "type": "success",
  "event": "callTimeout",
  "notice": {
    "kind": "timeoutCalled"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:endTimeout

ws:write

End the running timeout early because both teams are ready.

Refused while the match panel is locked (before the match starts or after it ends).

WhoAnyone who may call a team timeout.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab"
}
Example acknowledgement
{
  "type": "success",
  "event": "endTimeout",
  "notice": {
    "kind": "timeoutEnded"
  },
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}
emit

match:setPlayerVolume

ws:write

Mute or set the overlay audio volume of one player.

WhoStream hosts, staff and coordinators of the match.
Triggersmatch:update

Request payload

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
playerGuidrequireduuidA player on a team in this match.
teamPlayerLinkGuiduuidOptional: the exact team membership.
isMutedrequiredboolean
volumerequiredinteger0 to 100.

Acknowledgement

See MatchCommandAck.

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
  "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
  "playerGuid": "00000000-0000-4000-8000-000000000000",
  "teamPlayerLinkGuid": "00000000-0000-4000-8000-000000000000",
  "isMuted": true,
  "volume": 80
}
Example acknowledgement
{
  "type": "success",
  "event": "setPlayerVolume",
  "notice": null,
  "detail": {
    "revision": 1791503530504000,
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "…": "…"
  },
  "access": {
    "canReferee": true,
    "…": "…"
  }
}

Events you receive 3

on

match:update

MessagePack

A match changed. Carries the complete new detail.

Sent to both match rooms with the same revision: the staff room gets the full detail, the public room the public view (no pending scores, no realtime frame) and no notice for staff-only actions. Replace your copy when detail.revision is newer than yours; ignore older ones.

Roomsmatch:<matchGuid>match:<matchGuid>:public
WhenAny match:* command (or legacy alias) except publishRealtimeScores; REST match edits that change live state.

Payload

FieldTypeDescription
typerequired"success"
eventrequiredstringThe command that caused it.
noticeMatchNotice
detailrequiredMatchLiveDetail
Example payload
{
  "type": "success",
  "event": "confirmPlayingPlayers",
  "notice": {
    "kind": "playersConfirmed"
  },
  "detail": {
    "revision": 1791503530504001,
    "operations": {
      "phase": "awaiting_scores",
      "playersLocked": true,
      "…": "…"
    },
    "…": "…"
  }
}
on

match:realtime

MessagePack

Latest realtime TournamentAssistant score frame (staff room only).

High frequency and never persisted. Not a result: only submitted and confirmed scores count.

Roomsmatch:<matchGuid>
Whenmatch:publishRealtimeScores.

Payload

FieldTypeDescription
typerequired"success"
matchGuidrequireduuid
latestRealtimeScoresrequiredobject
playerPlatformIdsobjectSame as MatchOperationsState.playerPlatformIds.
timeoutAvailabilityMatchTimeoutAvailability
revisionrequiredinteger
updatedAtstring
on

match:settings

MessagePack

The tournament's match settings changed (permissions, scoring, TA default, Discord role).

Roomsmatch:<matchGuid>match:<matchGuid>:public
WhenMatch settings saved through REST, or the Discord integration's match role changed.

Payload

FieldTypeDescription
typerequired"success"
tournamentGuidrequireduuid
settingsrequiredMatchSettings

Overlays

Overlay snapshots and which match an overlay shows.

Packets you send 2

emit

getTournamentOverlay

ws:read

Load an overlay snapshot and follow it.

Joins the overlay rooms and the staff room of the overlay's current match. When the overlay is assigned another match the server moves the socket to that match's room automatically.

WhoOverlay operators: organisers, admins, staff, referees, head coordinators, stream hosts, global staff, and coordinators of the overlay's match.
Joins<tournamentGuid>:<overlayGuid><overlayGuid>match:<matchGuid>

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

emit

setTournamentOverlayMatch

ws:write

Show another match on the overlay.

The match's own workflow is untouched, so an overlay can join a match that is already live. Every viewer of the overlay is moved to the new match room.

WhoOrganisers, admins, staff, head coordinators, stream hosts and global staff.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
matchGuidrequireduuidA match of the same tournament.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

Events you receive 1

on

tournamentOverlayUpdate:match

MessagePack

The overlay now shows another match.

Your socket has already been moved to the new match's room; re-render from newOverlay.

Rooms<tournamentGuid>:<overlayGuid><overlayGuid>
WhensetTournamentOverlayMatch by another socket.

Payload

FieldTypeDescription
typerequired"success"
newOverlayrequiredOverlaySnapshot

Casters

Who is casting a match and who of them is speaking in the casters voice channel.

Packets you send 2

emit

casters:subscribe

public

Follow the casters of a match and who of them is speaking.

Matches with assigned casters (and "List assigned casters" on) show exactly those casters. Otherwise everyone in the tournament's casters channel is shown, except the match's stream hosts, coordinators and players.

The bot joins the casters channel (Match settings) while anyone is in it and only watches who sends audio. Subscribe again after reconnecting or when the overlay switches to another match.

WhoAnyone, signed in or not. The list only contains what overlays put on stream.
Joinscasters:<matchGuid>

Request payload

FieldTypeDescription
matchGuidrequireduuid

Acknowledgement

FieldTypeDescription
typerequired"success"
staterequiredMatchCastersState

Failures: SocketFailure.

Example request
{
  "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41"
}
Example acknowledgement
{
  "type": "success",
  "state": {
    "matchGuid": "4b1e2c8e-3f0a-4c1b-9d0e-6f2a8b7c5d41",
    "tournamentGuid": "c0ffee00-1234-4abc-8def-0123456789ab",
    "channelId": "1290000000000000000",
    "connected": true,
    "source": "assigned",
    "casters": [
      {
        "key": "…",
        "userGuid": "…",
        "discordId": "123456789012345678",
        "name": "Hawk",
        "avatarUrl": "https://…",
        "inChannel": true,
        "speaking": true,
        "muted": false
      }
    ],
    "updatedAt": "2026-10-09T18:00:00.000Z"
  }
}
emit

casters:unsubscribe

public

Stop following a match's casters.

WhoAnyone.
Leavescasters:<matchGuid>

Request payload

FieldTypeDescription
matchGuidrequireduuid

Acknowledgement

FieldTypeDescription
typerequired"success"

Failures: SocketFailure.

Events you receive 1

on

casters:update

MessagePack

The casters of a match, or who of them is speaking, changed. Carries the complete new state.

Sent only when something visible changed. Speaking ends a quarter of a second after the last audio packet, so short pauses between words do not flicker.

Roomscasters:<matchGuid>
WhenSomeone joins, leaves, mutes or starts or stops speaking in the casters channel; the bot joins or leaves; the match's staff assignments or the casters channel setting change.

Payload

FieldTypeDescription
typerequired"success"
staterequiredMatchCastersState

Overlay documents

Live editing of overlay scenes, components and nodes.

Packets you send 9

emit

subscribeTournamentOverlayDocuments

ws:read

Follow an overlay's documents (scenes, components, nodes) for live editing or rendering.

Also follows the overlay's match room, since scenes render that match.

WhoOverlay operators and coordinators of the overlay's match.
Joins<tournamentGuid>:<overlayGuid>match:<matchGuid>

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
roomrequiredstring
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

emit

createOverlayDocument

ws:write

Create a scene or reusable component document.

guid may be supplied (a UUID) for scenes imported from files; otherwise the server generates one. Width and height default to the overlay's size.

WhoOverlay operators.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
guiduuidOptional document GUID.
kindrequiredscene | component
namerequiredstring
widthPxinteger
heightPxinteger
sortOrderinteger
isDefaultboolean
publicPropertiesobjectComponent inputs exposed to instances.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
documentrequiredobject
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

emit

updateOverlayDocument

ws:write

Rename or resize a document, or change its order, default flag or public properties.

WhoOverlay operators.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
documentGuidrequireduuid
documentrequiredobject
document.namestring
document.widthPxinteger
document.heightPxinteger
document.sortOrderinteger
document.isDefaultboolean
document.publicPropertiesobject

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

emit

deleteOverlayDocument

ws:write

Delete a document and its nodes.

WhoOverlay operators.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
documentGuidrequireduuid

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

emit

setActiveOverlayScene

ws:write

Switch the scene the overlay shows.

WhoOverlay operators.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
sceneDocumentGuidrequireduuid

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

emit

createOverlayNode

ws:write

Add a node to a document.

The server always generates the node GUID and returns it as node.guid. Use that GUID for later updates, deletes, parents and undo; a local temporary id may be used for optimistic rendering but must never be sent as the node identity.

Parent and component references are validated: the parent must be in the same document, and component instances cannot form cycles.

WhoOverlay operators.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
documentGuidrequireduuid
noderequiredOverlayNodePatch & object

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
noderequiredobjectThe created node, with its server GUID.
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

emit

updateOverlayNode

ws:write

Patch one node.

WhoOverlay operators.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
documentGuidrequireduuid
nodeGuidrequireduuid
noderequiredOverlayNodePatch

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

emit

deleteOverlayNode

ws:write

Delete one node.

WhoOverlay operators.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
documentGuidrequireduuid
nodeGuidrequireduuid

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

emit

bulkUpdateOverlayNodes

ws:write

Patch several nodes of one document atomically (multi-select moves, reordering).

WhoOverlay operators.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
documentGuidrequireduuid
nodesrequiredobject[]
nodes[].nodeGuidrequireduuid
nodes[].noderequiredOverlayNodePatch

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot

Failures: SocketFailure.

Events you receive 1

on

tournamentOverlayDocuments:update

MessagePack

An overlay document or node changed. Carries the whole new overlay.

Rooms<tournamentGuid>:<overlayGuid>
WhenAny overlay document packet from another socket.

Payload

FieldTypeDescription
typerequired"success"
eventTyperequireddocument:create | document:update | document:delete | scene:active | node:create | node:update | node:delete | node:bulk-updateWhat changed.
newOverlayrequiredOverlaySnapshot

Custom overlay packets and states

Organiser-defined packets and saved states sent to overlays.

Packets you send 5

emit

getCustomOverlayStates

ws:read

Load the overlay's saved custom states and follow their updates.

Joins the overlay rooms before reading, so an update that happens during the request is still received. Read access does not grant write access.

WhoOverlay operators and coordinators of the overlay's match.
Joins<tournamentGuid>:<overlayGuid><overlayGuid>

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
overlayGuidrequireduuid
statesrequiredCustomStateSnapshot[]

Failures: SocketFailure.

emit

updateCustomOverlayState

ws:write

Change a saved custom state.

merge deep-merges an object into the current value; replace overwrites it and requires expectedRevision (optimistic concurrency). A preset may supply the value. Placeholders are resolved, then the value is validated against the state's schema.

WhoRoles allowed by the state definition (staff and referees by default; coordinators when allowCoordinatorStreamNotifications is on).

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
stateGuidrequireduuidA registered state of this overlay.
moderequiredmerge | replace
valueanyNew value (JSON).
presetIdstringOptional preset to start from.
expectedRevisionintegerRequired for replace; the revision you last saw.

Acknowledgement

FieldTypeDescription
typerequired"success"
staterequiredCustomStateSnapshot
validatedbooleanFalse: the state was saved.

Failures: SocketFailure.

emit

validateCustomOverlayState

ws:write

Dry-run updateCustomOverlayState: validate and preview without saving or broadcasting.

WhoSame as updateCustomOverlayState.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
stateGuidrequireduuidA registered state of this overlay.
moderequiredmerge | replace
valueanyNew value (JSON).
presetIdstringOptional preset to start from.
expectedRevisionintegerRequired for replace; the revision you last saw.

Acknowledgement

FieldTypeDescription
typerequired"success"
staterequiredCustomStateSnapshot
validatedbooleanTrue: nothing was saved.

Failures: SocketFailure.

emit

sendCustomOverlayPacket

ws:write

Send a registered custom packet to every viewer of an overlay.

The packet type must be registered on the overlay (REST: overlay custom packets). A preset may supply or be merged with the payload; placeholders are resolved and the result is validated against the packet schema.

Viewers receive customOverlayPacket (the full envelope) and also an event named after the packet type carrying just the payload. Integrations may emit the packet type directly as the event name with the same body; the server routes unknown event names here (needs ws:write).

WhoRoles allowed by the packet definition (staff and referees by default; coordinators when allowCoordinatorStreamNotifications is on).
TriggerscustomOverlayPacket<packetType>

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
packetTyperequiredstring
payloadanyJSON payload.
presetIdstring

Acknowledgement

FieldTypeDescription
typerequired"success"
packetrequiredCustomPacket
validatedboolean

Failures: SocketFailure.

emit

validateCustomOverlayPacket

ws:write

Dry-run sendCustomOverlayPacket without broadcasting.

WhoSame as sendCustomOverlayPacket.

Request payload

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
packetTyperequiredstring
payloadany
presetIdstring

Acknowledgement

FieldTypeDescription
typerequired"success"
packetrequiredCustomPacket
validatedboolean

Failures: SocketFailure.

Events you receive 4

on

customOverlayStateUpdate

MessagePack

A saved custom state changed.

Rooms<tournamentGuid>:<overlayGuid><overlayGuid>
WhenupdateCustomOverlayState, or a REST state update.

Payload

See CustomStateSnapshot.

on

customOverlayStatesChanged

MessagePack

The overlay's state registry changed (states added, edited or removed). Reload with getCustomOverlayStates.

Rooms<tournamentGuid>:<overlayGuid>
WhenREST custom state configuration.

Payload

FieldTypeDescription
overlayGuidrequireduuid
on

customOverlayPacket

MessagePack

A custom packet was sent to the overlay (full envelope).

The same send also emits an event named after packetType with only the payload, for renderers that listen per type.

Rooms<tournamentGuid>:<overlayGuid><overlayGuid>
WhensendCustomOverlayPacket or a direct packet-type emit.

Payload

See CustomPacket.

on

customOverlayPacketsChanged

MessagePack

The overlay's custom packet registry changed.

Rooms<tournamentGuid>:<overlayGuid>
WhenREST custom packet configuration.

Payload

FieldTypeDescription
overlayGuidrequireduuid

Live co-editing

Operational-transform editing of tournament text, update drafts and match notes.

Packets you send 10

emit

collab:join

ws:write

Join a live editing session and receive every field's text and revision.

Joins the session room before snapshotting, so no operation falls between the snapshot and the subscription. After a reconnect, pass resume: { epoch, revisions } from your previous join: when the server still has the history it returns missedOperations per field instead of forcing a full reload. A different epoch means the session restarted; use the returned documents.

Completed and cancelled tournaments answer with readOnly: true.

WhoEdit access for the scope.
Joinscollab:<scope>:<guid>

Request payload

FieldTypeDescription
scoperequiredCollaborationScope
resumeobject
resume.epochstring
resume.revisionsobjectField name → last revision you applied.

Acknowledgement

FieldTypeDescription
typerequired"success"
roomrequiredstringcollab:general:<tournamentGuid>, collab:updates:<tournamentGuid>, collab:match-notes:<matchGuid>, collab:doc:<docGuid>, collab:form:<formGuid> or collab:sheet:<sheetGuid>.
epochrequiredstringSession instance id.
clientIdrequiredstringYour participant id (socket id).
readOnlybooleanTrue when the session is locked (finished tournament) or you only have view access.
readOnlyReasonstring
documentsrequiredobjectField name → document. Update drafts use <draftGuid>:title and <draftGuid>:content.
participantsrequiredCollaborationParticipant[]
staterequiredobjectScope state: tournament settings, update drafts, or earlier notes of the same teams (previousNotes).
missedOperationsobjectField → operations after your resume revision.
sheetobject
sheet.revisionrequiredintegerApply collab:sheet:op events with the next revision.
sheet.datarequiredobjectEvery tab: { version: 1, tabs: [{ id, name, color, rows, cols, frozenRows, frozenCols, colWidths, rowHeights, cells }] }. Cells are keyed rowId:colId.

Failures: SocketFailure.

emit

collab:leave

ws:write

Leave a session.

The last participant leaving flushes unsaved text; the session stays in memory for a minute so a quick reconnect can resume.

WhoParticipants.
Leavescollab:<scope>:<guid>

Request payload

FieldTypeDescription
roomrequiredstring

Acknowledgement

FieldTypeDescription
typerequired"success"

Failures: SocketFailure.

emit

collab:edit

ws:write

Apply a text operation to one field.

revision is the last server revision you have applied for that field. The server transforms your operation against newer history, applies it, acknowledges the new revision and broadcasts collab:operation to everyone else. Resending the same opId is safe (idempotent).

Handle the acknowledgement in the synchronous ack callback, not a promise chain: acks and broadcasts can arrive in one chunk and reordering them corrupts your confirmed text. A failure with resync: true means: leave and join again.

Edits are saved about 0.8 s after typing stops (at most every 5 s while typing) and announced with collab:saved.

WhoParticipants of a writable session.

Request payload

FieldTypeDescription
roomrequiredstring
fieldrequiredstringField name from the join documents.
opIdrequiredstringUnique id for this operation (max 64 chars).
revisionrequiredinteger
operationrequiredTextOperation
selectionobjectYour caret after the edit (optional).
selection.fieldstring
selection.anchorinteger
selection.headinteger

Acknowledgement

FieldTypeDescription
typerequired"success"
revisionrequiredintegerThe revision your operation became.

Failures: SocketFailure.

Example request
{
  "room": "collab:general:c0ffee00-…",
  "field": "description",
  "opId": "k3j2-17",
  "revision": 41,
  "operation": [
    12,
    "Hello ",
    -3,
    200
  ]
}
Example acknowledgement
{
  "type": "success",
  "revision": 42
}
emit

collab:presence

ws:write

Share which field you are in and your selection.

WhoParticipants.

Request payload

FieldTypeDescription
roomrequiredstring
focusstring | nullField name, max 200 chars.
selectionobject | null
selection.fieldstring
selection.anchorinteger
selection.headinteger

Acknowledgement

FieldTypeDescription
typerequired"success"

Failures: SocketFailure.

emit

collab:draft:create

ws:write

Create an update draft (updates workspace only).

Adds the fields <draftGuid>:title and <draftGuid>:content. A tournament keeps at most 50 drafts.

WhoParticipants of collab:updates:<tournamentGuid>.
Triggerscollab:state

Request payload

FieldTypeDescription
roomrequiredstring

Acknowledgement

FieldTypeDescription
typerequired"success"
draftrequiredobject

Failures: SocketFailure.

emit

collab:draft:delete

ws:write

Delete an update draft.

WhoParticipants of the updates workspace.

Request payload

FieldTypeDescription
roomrequiredstring
draftGuidrequireduuid

Acknowledgement

FieldTypeDescription
typerequired"success"

Failures: SocketFailure.

emit

collab:draft:change

ws:write

Add, replace or remove one structured change card on a draft.

Exactly one of upsert (a change object with a guid) or remove (a change guid). Cards are edited individually so concurrent card edits do not overwrite each other.

WhoParticipants of the updates workspace.
Triggerscollab:state

Request payload

FieldTypeDescription
roomrequiredstring
draftGuidrequireduuid
upsertobjectChange card.
removestringChange guid.

Acknowledgement

FieldTypeDescription
typerequired"success"
draftrequiredobject

Failures: SocketFailure.

emit

collab:draft:publish

ws:write

Publish a draft as a tournament update.

Locks the draft fields, flushes the final text and publishes it with the same GUID. sendToDiscord: true also posts it through the Discord integration.

WhoOrganisers, admins, staff, head coordinators and head poolers.

Request payload

FieldTypeDescription
roomrequiredstring
draftGuidrequireduuid
sendToDiscordboolean

Acknowledgement

FieldTypeDescription
typerequired"success"
updaterequiredobjectThe published update (as returned by REST).

Failures: SocketFailure.

emit

collab:form:change

ws:write

Change a form's structure: add, duplicate, retype, move or remove questions and options.

Texts (titles, descriptions, option labels) are live fields edited with collab:edit; this packet changes everything else. New questions and options join the session as live fields and are announced to everyone through collab:state (documents added, removedDocuments removed, event saying what happened).

change.kind is one of addQuestion { type, index?, required?, settings? }, duplicateQuestion { questionGuid }, updateQuestion { questionGuid, type?, required?, settings? }, removeQuestion { questionGuid }, moveQuestion { questionGuid, index }, addOption { questionGuid, index?, label? }, removeOption { questionGuid, optionGuid }, moveOption { questionGuid, optionGuid, index }. Settings are normalized per question type (unknown keys dropped, numbers clamped).

WhoEdit access to the form (participants of collab:form:<formGuid>).
Triggerscollab:state

Request payload

FieldTypeDescription
roomrequiredstringcollab:form:<formGuid>
changerequiredobject
change.kindrequiredaddQuestion | duplicateQuestion | updateQuestion | removeQuestion | moveQuestion | addOption | removeOption | moveOption
change.typesection | short_text | long_text | number | single_choice | multiple_choice | dropdown | scale | rating | date | beatsaver_map | tournament_pool | tournament_map | tournament_teamQuestion type (add/update).
change.questionGuiduuid
change.optionGuiduuid
change.indexintegerTarget position (add/move). Omit to append.
change.requiredboolean
change.settingsobjectType settings, e.g. { min, max, minLabel, maxLabel } for a scale or { askReason: true, reasonLabel } for an "and why?" follow-up.
change.labelstringInitial option label (addOption).

Acknowledgement

FieldTypeDescription
typerequired"success"
eventrequiredobjectWhat changed, e.g. { type: "question:added", questionGuid }.
staterequiredobjectThe form's new structure.

Failures: SocketFailure.

Example request
{
  "room": "collab:form:7d1e…",
  "change": {
    "kind": "addQuestion",
    "type": "scale",
    "index": 2,
    "settings": {
      "min": 1,
      "max": 5,
      "minLabel": "Rough",
      "maxLabel": "Smooth"
    }
  }
}
Example acknowledgement
{
  "type": "success",
  "event": {
    "type": "question:added",
    "questionGuid": "…"
  },
  "state": {
    "…": "…"
  }
}
emit

collab:sheet:change

ws:write

Change a shared sheet: cells, formatting, rows, columns, sizes, frozen panes or tabs.

Rows and columns have stable ids and cells are keyed rowId:colId, so concurrent edits never shift each other. Changes are applied in arrival order (the last write to a cell wins) and broadcast to everyone, including you, as collab:sheet:op with the next revision. Edits that point at rows, columns or tabs someone else removed are skipped. Resending the same opId is safe.

change.kind is one of setCells { tab, cells: [{ row, col, value?, format?, clearFormat? }] }, insertRows|insertCols { tab, index, ids, cells?, sizes? }, deleteRows|deleteCols { tab, ids }, rowOrder { tab, rows } (e.g. after sorting), resize { tab, cols?, rows? }, freeze { tab, rows, cols }, addTab { tab, name, index, rows, cols }, updateTab { tab, name?, color? }, deleteTab { tab }, moveTab { tab, index }. Format patches switch b, i, u, s (strikethrough) and w (wrap) with booleans and set c (text colour), bg (fill), a (left/center/right) and va (top/middle/bottom), or remove them with null.

Limits: 20 tabs, 5,000 rows and 200 columns per tab, 250,000 filled cells, 10,000 characters per cell, 25,000 cells per change. The same model ships in bk-client (applySheetChange).

WhoEdit access to the sheet (participants of collab:sheet:<sheetGuid> who are not read-only).

Request payload

FieldTypeDescription
roomrequiredstringcollab:sheet:<sheetGuid>
opIdrequiredstringYour id for this change (at most 64 characters).
changerequiredobject
change.kindrequiredsetCells | insertRows | insertCols | deleteRows | deleteCols | rowOrder | resize | freeze | addTab | updateTab | deleteTab | moveTab
change.tabrequiredstringTab id.
change.cellsobjectsetCells: list of cell edits; insert: cells to restore keyed rowId:colId.
change.idsstring[]Row or column ids (insert/delete).
change.indexinteger

Acknowledgement

FieldTypeDescription
typerequired"success"
revisionrequiredinteger
duplicateboolean

Failures: SocketFailure.

Example request
{
  "room": "collab:sheet:5b2c…",
  "opId": "k2j4…",
  "change": {
    "kind": "setCells",
    "tab": "q8w1e2r3t4",
    "cells": [
      {
        "row": "a1b2c3d4e5",
        "col": "z9y8x7w6v5",
        "value": "Seed",
        "format": {
          "b": true,
          "bg": "#fff2cc"
        }
      }
    ]
  }
}
Example acknowledgement
{
  "type": "success",
  "revision": 42,
  "duplicate": false
}

Events you receive 5

on

collab:operation

MessagePack

Another participant's edit (or text saved through REST). Apply it in order.

revision is the field's new revision. Transform it against your unacknowledged local operations before applying, as in bk-client's CollaborativeText. clientId: "server" means the text came from a REST save.

Roomscollab:<scope>:<guid>
Whencollab:edit from another socket; REST saves of the same text.

Payload

FieldTypeDescription
roomrequiredstring
fieldrequiredstring
revisionrequiredinteger
operationrequiredTextOperation
opIdrequiredstring
clientIdrequiredstring
selectionobject | null
on

collab:sheet:op

MessagePack

A sheet change was applied. Apply it in revision order.

change is the normalized change (skipped cells removed, values clamped). It also reaches the sender, which drops its matching optimistic change by opId. A revision gap means you missed changes; join again for a fresh snapshot.

Roomscollab:sheet:<sheetGuid>
Whencollab:sheet:change.

Payload

FieldTypeDescription
roomrequiredstring
revisionrequiredinteger
changerequiredobject
opIdrequiredstring
clientIdrequiredstring
on

collab:participants

MessagePack

Who is in the session, with focus and selections.

Roomscollab:<scope>:<guid>
WhenJoin, leave, disconnect, presence changes, draft deletion.

Payload

FieldTypeDescription
roomrequiredstring
participantsrequiredCollaborationParticipant[]
on

collab:saved

MessagePack

A field was saved to the database (or saving failed).

Roomscollab:<scope>:<guid>
WhenAutosave after edits.

Payload

FieldTypeDescription
roomrequiredstring
fieldrequiredstring
revisionrequiredinteger
savedAtstring | null
errorstring | nullShown to the user; saving retries with the next edit.
on

collab:state

MessagePack

The session's structured state changed (settings saved, drafts created, changed, deleted or published).

Roomscollab:<scope>:<guid>
WhenDraft packets; collab:form:change; REST saves of tournament settings and doc, sheet and form settings.

Payload

FieldTypeDescription
roomrequiredstring
staterequiredobject
documentsobjectNew fields (draft created).
removedDocumentsstring[]Fields that no longer exist.
eventobject
event.typesettings:saved | draft:created | draft:deleted | draft:published | doc:settings | sheet:settings | form:settings | question:added | question:updated | question:removed | question:moved | option:added | option:removed | option:moved
event.draftGuiduuid
event.questionGuiduuid
event.optionGuiduuid
event.updateobject
event.byobject

Donations

Live donation and prize pool updates.

Packets you send 2

emit

donation:subscribe

ws:read

Follow a tournament's donations and prize pool.

The acknowledgement contains the full public donation snapshot (same shape as the public REST donations query).

Joins<tournamentGuid>:donations

Request payload

FieldTypeDescription
tournamentGuidrequireduuid

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
roomrequiredstring<tournamentGuid>:donations
tournamentrequiredobjectPublic donation snapshot: donations, prizePool totals, page URLs.

Failures: SocketFailure.

emit

donation:unsubscribe

ws:read

Stop following donations.

Leaves<tournamentGuid>:donations

Request payload

FieldTypeDescription
tournamentGuidrequireduuid

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
roomrequiredstring

Failures: SocketFailure.

Events you receive 1

on

tournament:donation

MessagePack

A donation arrived, changed or was removed, or the prize pool settings changed.

Rooms<tournamentGuid>:donations
WhenKo-fi/PayPal webhooks and REST donation management.

Payload

FieldTypeDescription
typerequired"success"
kindrequireddonation:new | donation:modified | donation:removed | donation:settings
tournamentrequiredobjectThe complete public donation snapshot.
donationobjectThe affected donation (anonymised when not public).

Awards

Awards show category control.

Packets you send 2

emit

subAwards

ws:read

Join the awards show listener room.

Whorole:dev, role:owner or global:votes:view.
JoinsawardsListener

Request payload

No payload (send {}).

Acknowledgement

FieldTypeDescription
typerequired"success"
error""

Failures: SocketFailure.

emit

showCategory

ws:write

Reveal an awards category on every awards screen.

Whorole:dev, role:owner or global:votes:view.
TriggersshowCategory

Request payload

FieldTypeDescription
categoryrequiredstringCategory key.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
categoryrequiredstring

Failures: SocketFailure.

Events you receive 1

on

showCategory

MessagePack

Reveal an awards category.

RoomsawardsListener
WhenshowCategory packet.

Payload

FieldTypeDescription
typerequired"success"
error""
categoryrequiredstring

TournamentAssistant mock clients

Development tooling that runs fake TA clients.

Packets you send 6

emit

TAMock:GetFilters

ws:read

Read the tournament-name allow list for mock clients.

Whoglobal:taMock:modify.

Request payload

No payload (send {}).

Acknowledgement

FieldTypeDescription
typerequired"success"
filtersrequiredobject
filters.allowedTournamentNamesstring[]
filters.defaultDenyboolean

Failures: SocketFailure.

emit

TAMock:ModifyExplicitFilter

ws:write

Replace the tournament-name allow list.

Whoglobal:taMock:modify and not global:taMock:banned.

Request payload

FieldTypeDescription
allowedTournamentNamesrequiredstring[]
defaultDenyrequiredboolean

Acknowledgement

FieldTypeDescription
typerequired"success"
messagestring
filtersrequiredobject

Failures: SocketFailure.

emit

TAMock:GetData

ws:read

List your mock clients and limits (everyone's for global managers).

Request payload

No payload (send {}).

Acknowledgement

FieldTypeDescription
typerequired"success"
datarequiredobjectrequester (limits, flags) and instances.

Failures: SocketFailure.

emit

TAMock:ModifyUserClients

ws:write

Start, stop, restart or remove mock clients; global managers may also set limits and bans.

Request payload

FieldTypeDescription
userIdrequiredstringUser GUID or Discord ID.
newClientMaxCountintegerGlobal managers only.
isBannedbooleanGlobal managers only.
clientsrequiredobject[]
clients[].guidrequireduuid
clients[].userGuidrequireduuid
clients[].executeStartboolean
clients[].executeStopboolean
clients[].executeRemoveboolean
clients[].executeRestartboolean

Acknowledgement

FieldTypeDescription
typerequired"success"
messagestring
userobject
instancesobject[]
updatesobject[]

Failures: SocketFailure.

emit

TAMock:SubClientLogs

ws:read

Stream a mock client's logs.

WhoThe client's owner or a global manager.
JoinsTAMock-<clientGuid>

Request payload

FieldTypeDescription
clientGuidrequireduuid

Acknowledgement

FieldTypeDescription
typerequired"success"
messagestring
instancerequiredobject

Failures: SocketFailure.

emit

TAMock:Instantiate

ws:write

Create and start a mock TournamentAssistant client.

Limited by your mockClientLimit and the tournament-name allow list. Logs stream as TAMock:Instance:Data.

WhoAny user who is not global:taMock:banned.

Request payload

FieldTypeDescription
tournamentNamerequiredstringMax 255 characters.
addressstring
portstring
countinteger
userIdsstring[]
usernamesstring[]

Acknowledgement

FieldTypeDescription
typerequired"success"
messagestring
instancerequiredobject

Failures: SocketFailure.

Events you receive 1

on

TAMock:Instance:Data

MessagePack

A chunk of mock client log output.

RoomsTAMock-<clientGuid>
WhenMock client process output.

Payload

FieldTypeDescription
typerequired"update"
instanceGuidrequireduuid
statusstring
logChunkrequiredstring

Legacy and retired packets

Overlay-scoped names from before coordination moved to matches. Prefer the match:* packets.

Packets you send 26

emit

startSelectedMatch

ws:writealias of match:start

Legacy alias of match:start, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:start } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:start.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

endSelectedMatch

ws:writealias of match:end

Legacy alias of match:end, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:end } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:end.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

setTournamentOverlayMatchDetails

ws:writealias of match:setDetails

Legacy alias of match:setDetails, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:setDetails } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:setDetails.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

setPlayerVolumeSettings

ws:writealias of match:setPlayerVolume

Legacy alias of match:setPlayerVolume, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:setPlayerVolume } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:setPlayerVolume.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

resetPicksBansOverlayState

ws:writealias of match:resetPicksBans

Legacy alias of match:resetPicksBans, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:resetPicksBans } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:resetPicksBans.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

addPicksBansOverlayAction

ws:writealias of match:addPicksBansAction

Legacy alias of match:addPicksBansAction, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:addPicksBansAction } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:addPicksBansAction.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

removePicksBansOverlayAction

ws:writealias of match:removePicksBansAction

Legacy alias of match:removePicksBansAction, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:removePicksBansAction } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:removePicksBansAction.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

setMatchSide1

ws:writealias of match:setSide1

Legacy alias of match:setSide1, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:setSide1 } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:setSide1.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

setPicksBansVisibility

ws:writealias of match:setPicksBansVisibility

Legacy alias of match:setPicksBansVisibility, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:setPicksBansVisibility } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:setPicksBansVisibility.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

setPlayingPlayers

ws:writealias of match:setPlayingPlayers

Legacy alias of match:setPlayingPlayers, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:setPlayingPlayers } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:setPlayingPlayers.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

confirmPlayingPlayers

ws:writealias of match:confirmPlayingPlayers

Legacy alias of match:confirmPlayingPlayers, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:confirmPlayingPlayers } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:confirmPlayingPlayers.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

cancelAwaitingScores

ws:writealias of match:cancelAwaitingScores

Legacy alias of match:cancelAwaitingScores, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:cancelAwaitingScores } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:cancelAwaitingScores.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

setWarmupMode

ws:writealias of match:setWarmupMode

Legacy alias of match:setWarmupMode, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:setWarmupMode } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:setWarmupMode.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

setTournamentAssistantRoom

ws:writealias of match:setTournamentAssistantRoom

Legacy alias of match:setTournamentAssistantRoom, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:setTournamentAssistantRoom } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:setTournamentAssistantRoom.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

publishRealtimeScores

ws:writealias of match:publishRealtimeScores

Legacy alias of match:publishRealtimeScores, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:publishRealtimeScores } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:publishRealtimeScores.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

savePendingScoreDrafts

ws:writealias of match:savePendingScoreDrafts

Legacy alias of match:savePendingScoreDrafts, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:savePendingScoreDrafts } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:savePendingScoreDrafts.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

submitPendingScores

ws:writealias of match:submitPendingScores

Legacy alias of match:submitPendingScores, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:submitPendingScores } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:submitPendingScores.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

reviewPendingScores

ws:writealias of match:reviewPendingScores

Legacy alias of match:reviewPendingScores, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:reviewPendingScores } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:reviewPendingScores.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

resetMatch

ws:writealias of match:reset

Legacy alias of match:reset, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:reset } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:reset.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

saveMatchMapScores

ws:writealias of match:saveMapScores

Legacy alias of match:saveMapScores, acting on the overlay's current match.

Takes { tournamentGuid, overlayGuid, …fields of match:saveMapScores } instead of matchGuid, and answers with the overlay snapshot (overlay and newOverlay) instead of the match detail. Fails when the overlay has no match.

WhoSame as match:saveMapScores.
Triggersmatch:update

Request payload

See OverlayTarget.

Acknowledgement

FieldTypeDescription
typerequired"success"
error""
overlayrequiredOverlaySnapshot
newOverlayOverlaySnapshot

Failures: SocketFailure.

emit

setTournamentOverlayPermissions

ws:writeremoved

Removed. Always fails with an explanation.

Answers: Coordinator permissions are now tournament-wide. Change them in the tournament's Match Settings.

emit

submitMatchScores

ws:writeremoved

Removed. Always fails with an explanation.

Answers: submitMatchScores was removed. Use match:submitPendingScores.

emit

match:setFlow

ws:writeremoved

Removed. Always fails with an explanation.

Answers: The picks and bans flow is part of the match's settings. Tournament staff set it when editing the match, like its map pool.

emit

setPicksBansOverlayFlow

ws:writeremoved

Removed. Always fails with an explanation.

Answers: The picks and bans flow is part of the match's settings. Tournament staff set it when editing the match, like its map pool.

emit

match:setTournamentAssistantTournament

ws:writeremoved

Removed. Always fails with an explanation.

Answers: Every match uses the TournamentAssistant tournament from the tournament's Match settings.

emit

setMatchOverlayTAData

ws:writeremoved

Removed. Always fails with an explanation.

Answers: Every match uses the TournamentAssistant tournament from the tournament's Match settings.

Types

Shared payload types referenced above.

type

SocketFailure

Every packet answers failures with this shape (except authenticate, which uses success: false).

FieldTypeDescription
typerequired"fail"
errorrequiredstringHuman-readable reason. Safe to show to the user.
requiredScopews:read | ws:writePresent when the token lacks the scope this packet needs.
resyncbooleanCollaboration only: your copy is out of date; leave and join the session again.
statusintegerCustom overlay states only: an HTTP-like status (403, 404) for the failure.
type

MatchTarget

FieldTypeDescription
matchGuidrequireduuidThe match to act on.
tournamentGuidstringOptional sanity check: tournament GUID or id. Rejected when the match belongs to another tournament.
type

MatchAccess

What the caller may do on this match. Computed per request from tournament roles, match assignments and the match settings.

FieldTypeDescription
authenticatedboolean
canViewPanelbooleanMay open the coordinator panel: assigned, or holding the role while nobody is assigned.
canCoordinatebooleanCoordinator actions: playing players, picks and bans, realtime frames, score drafts.
canRefereebooleanReferee authority, including coordinators granted referee permissions in the match settings.
hasRefereeRolebooleanHolds referee access in their own right (assigned, role, or elevated).
canSubmitScoresboolean
canStartbooleanStream hosts, head coordinators, organisers, admins, staff and global staff.
canEndboolean
canResetMatchboolean
canManageFlowboolean
canEditNotesboolean
canSendStreamNotificationsboolean
canManageDiscordRoleboolean
isHostboolean
elevatedbooleanOrganiser, admin, staff or head coordinator of the tournament, or global staff.
type

MatchNotice

A short, displayable description of what just happened, for toasts and stream notifications. null when nothing notable happened.

FieldTypeDescription
kindrequiredmatchStarted | matchEnded | sides | playersConfirmed | warmupStarted | warmupEnded | scoresSubmitted | scoresConfirmed | replay | scoresDiscarded | matchReset | scoresEdited | pointsChanged | mapPick | mapBan | tiebreaker | clearMaps | decideTeams | flairPick | flairBan | actionUndone | picksBansReset | discordRoleGiven | discordRoleRemoved | playersDeafened | playersUndeafened | timeoutCalled | timeoutEnded
teamGuiduuid | nullTeam involved (sides, replay, picks and bans).
mapGuiduuid | nullMap involved (scores, picks and bans).
flairGuiduuid | null
winnerTeamGuiduuid | nullTeam awarded the map point.
flowOrderintegeractionUndone: the first removed flow step.
actorNamestringDiscord role and deafen notices: who triggered it.
totalintegerDiscord role and deafen notices: players targeted.
changedinteger
unchangedinteger
missingstring[]Player names without a linked Discord member.
failedstring[]Player names Discord refused.
notInVoicestring[]Deafen notices: player names not connected to a voice channel.
type

MatchOperationsScore

One player's completed play.

FieldTypeDescription
userGuidrequireduuidPlayer.
teamGuidrequireduuidThe player's team in this match.
playerNamestring
scorerequiredinteger
accuracyrequirednumberPercentage, 0 to 100.
completionTyperequiredintegerTournamentAssistant completion type.
missesrequiredinteger
badCutsrequiredinteger
goodCutsrequiredinteger
endTimerequirednumberSong time (seconds) when the play ended.
mapGuiduuid | nullBeatKhana map, when the played map is in the pool.
mapHashIfNotMapstring | nullLevel hash when the map is not a BeatKhana map.
isCallingReplaybooleanReferee submissions: mark every score of exactly one team to call a replay for that team.
isCountedbooleanFalse keeps the score on record without counting it. Only referees decide this.
notCountedReasonstring | null
type

MatchMapScoreEdit

FieldTypeDescription
scoreGuiduuid | nullExisting score to update. Omit to add a new score.
userGuidrequireduuid
scorerequirednumber
accuracyrequirednumber
missesrequiredinteger
badCutsrequiredinteger
completionTypeinteger
isCountedboolean
notCountedReasonstring | null
type

MatchOperationsState

FieldTypeDescription
phaserequiredplayers_selection | awaiting_scores | score_reviewScore workflow phase.
side1TeamGuiduuid | nullTeam on logical Side 1 for picks and bans.
showPicksBansbooleanWhether overlays should show the picks and bans.
warmupActiveboolean
warmupCompletedboolean
playersLockedrequiredbooleanTrue once a referee confirmed the playing players.
activePlayerGuidsrequireduuid[]Players currently selected to play.
currentRoomGuiduuid | nullTournamentAssistant room in use.
pendingScoresMatchOperationsScore[]Staff room only. Empty in the public view.
pendingScoreCountintegerNumber of pending scores (also in the public view).
latestRealtimeScoresobjectStaff room only: the latest realtime frame, keyed by player. Empty in the public view.
playerPlatformIdsobjectStaff room only: BeatKhana user GUID -> the platform ID the player is connected to TournamentAssistant with (TA replay streams use this ID). Kept across score resets. Empty in the public view.
timeoutMatchTimeout
timeoutAvailabilityMatchTimeoutAvailability
serverTimestringISO time on the server when the state was built, to count timeouts down against the server's clock.
updatedAtstringISO time of the last workflow change.
type

MatchTimeout

The running timeout, also in the public view. It ends at endsAt, or earlier with match:endTimeout.

FieldTypeDescription
guidrequireduuid
kindrequiredteam | technical
teamGuiduuid | nullThe team that called it; null for a technical timeout.
durationSecondsrequiredinteger
startedAtrequiredstringISO time.
endsAtrequiredstringISO time play resumes.
calledByNamestring | null
type

MatchTimeoutAvailability

Staff room only. Also sent with realtime frames, since entering or leaving a map changes it.

FieldTypeDescription
situationrequiredidle | flow_action | before_play | playing | replay_decision
allowedrequiredbooleanWhether a team may call a timeout now under the match settings.
reasonstring | nullWhy not, in one sentence.
type

MatchPanelState

Panels unlock between an actual start time (after 2020) and a later actual end time.

FieldTypeDescription
phaserequirednot_started | live | concluded
unlockedrequiredbooleanCoordinators and referees may operate the match.
locksAtstring | nullISO time a live match locks on its own (a future end time).
type

MatchSettings

Tournament-wide coordination rules (tournament_match_settings).

FieldTypeDescription
tournamentGuidrequireduuid
coordinatorHasRefereePermissionsboolean
allowCoordinatorSetScoreboolean
allowCoordinatorManageMatchboolean
allowCoordinatorStreamNotificationsboolean
allowCoordinatorTimeoutStartboolean
scoringobjectScoring rule: metric (score, accuracy, misses + bad cuts), aggregation (sum or average) and direction.
taTournamentGuiduuid | nullDefault TournamentAssistant tournament.
gameplayobjectDefault modifiers, Disable Fail and Disable Pause.
discordMatchRolebooleanA Discord Current Match Role is configured.
castersChannelIdstring | nullVoice channel in the linked Discord server where casters sit.
updatedAtstring | null
type

MatchCastersState

The casters an overlay shows for a match and who of them is speaking.

FieldTypeDescription
matchGuidrequireduuid
tournamentGuidrequireduuid
channelIdrequiredstring | nullThe tournament's casters channel, or null when none is set.
connectedrequiredbooleanThe bot is in the casters channel, so speaking is live.
sourcerequiredassigned | channelassigned: the match's assigned casters. channel: everyone in the casters channel except the match's stream hosts, coordinators and players.
castersrequiredobject[]
casters[].keyrequiredstringStable render key: the BeatKhana user GUID, else the Discord id.
casters[].userGuiduuid | null
casters[].discordIdrequiredstring
casters[].namerequiredstring
casters[].avatarUrlrequiredstring
casters[].inChannelrequiredboolean
casters[].speakingrequiredboolean
casters[].mutedrequiredbooleanMuted or deafened in Discord.
updatedAtstring
type

MatchLiveDetail

The complete live state of one match.

FieldTypeDescription
revisionrequiredintegerMonotonic. Ignore a detail older than the one you hold.
matchGuidrequireduuid
tournamentGuiduuid
matchrequiredobjectThe match record with teams, members, map pool, picks and bans, scores and assignments. The public view omits pending scores and other bracket matches.
operationsrequiredMatchOperationsState
picksBansFlowobject | nullThe selected picks and bans flow.
settingsrequiredMatchSettings
panelrequiredMatchPanelState
taTournamentGuiduuid | nullThe match's TournamentAssistant tournament, falling back to the tournament default.
serverTimestringISO server time, for clock-skew correction.
isPrivaterequiredbooleanTrue in the staff room (full workflow).
type

MatchCommandAck

FieldTypeDescription
typerequired"success"
eventrequiredstringThe command that ran, e.g. submitPendingScores.
noticeMatchNotice
detailrequiredMatchLiveDetailThe new private detail (same revision as the broadcast). null for publishRealtimeScores.
accessrequiredMatchAccess
type

OverlayTarget

FieldTypeDescription
tournamentGuidrequiredstringTournament GUID (or, where accepted, its URL id). The server always resolves the tournament from the match or overlay and rejects a mismatch.
overlayGuidrequireduuidOverlay GUID.
type

OverlaySnapshot

The overlay with its documents (scenes and components) and nodes, the assigned match, and fields derived from that match: matchOperationsState, assignedPicksBansFlow, taServerGuid, taMatchGuid. Fold later match:update details in with bk-client mergeMatchDetailIntoOverlay.

Free-form object.

type

OverlayNodePatch

Partial node. Only known node columns are stored; see OverlayNodePatch in src/modals/websocket/overlayTypes.ts for every field.

FieldTypeDescription
parentNodeGuiduuid | nullParent node in the same document.
nodeTypeleaf | component_instance
componentKindstring | nullRenderer, e.g. text, image, songCard, scoreDisplay, picksBansGrid, videoPlayer, custom.
sourceDocumentGuiduuid | nullComponent document for a component instance. Cycles are rejected.
namestring
posXnumber
posYnumber
widthPxnumber
heightPxnumber
rotationDegnumber
opacitynumber
zIndexinteger
isVisibleboolean
propsobjectRenderer-specific options.
appearanceobject
bindingsobjectData bindings (team, player, map index...).
editorMetaobject
type

TextOperation

Operational transform spanning the whole document: positive integer = retain n, negative integer = delete n, string = insert. Indices are UTF-16 code units.

Array of integer | string.

type

CollaborationScope

Which text to edit together.

One of:

Tournament description and rules. Needs general-settings edit access.

FieldTypeDescription
typerequired"tournament-general"
tournamentGuidrequireduuid

Update drafts. Needs update edit access.

FieldTypeDescription
typerequired"tournament-updates"
tournamentGuidrequireduuid

Private match notes. Needs canEditNotes on the match.

FieldTypeDescription
typerequired"match-notes"
tournamentGuidrequireduuid
matchGuidrequireduuid

A shared doc (fields title, description, content). Viewers join read-only and see edits live; editors can type.

FieldTypeDescription
typerequired"doc"
docGuidrequireduuid

A shared form (fields title, description, confirmation, q:<questionGuid>:title, q:<questionGuid>:description, o:<optionGuid>). Needs edit access.

FieldTypeDescription
typerequired"form"
formGuidrequireduuid

A shared sheet (text fields title, description; cells arrive as sheet in the join response and change through collab:sheet:change). Viewers join read-only.

FieldTypeDescription
typerequired"sheet"
sheetGuidrequireduuid
type

CollaborationParticipant

FieldTypeDescription
clientIdstringThe participant's socket id.
userobject
user.guiduuid
user.namestring
user.avatarUrlstring | null
colorstringPresence colour.
focusstring | nullField the participant is in.
selectionobject | null
selection.fieldstring
selection.anchorinteger
selection.headinteger
joinedAtstring
type

CollaborationDocument

FieldTypeDescription
contentstring
revisionintegerServer revision of this field.
maxLengthinteger
savedAtstring | null
type

CustomStateSnapshot

A saved overlay state: guid, stateKey, value, revision, updatedAt, updatedByGuid.

Free-form object.

type

CustomPacket

FieldTypeDescription
packetTypestringRegistered packet type (also the Socket.IO event name overlays listen to).
payloadanyValidated against the packet's schema, after presets and placeholders are applied.
tournamentGuiduuid
overlayGuiduuid
sentAtstring
sentByGuiduuid