Skip to content
Micup API

Table games

The room theme of a guild voice channel can place game tables in its room, one for each table slot such as T01. The members connected to the channel play Classic Okey, 101 Okey, chess, Türk daması, Normal dama, or tavla at them. The server holds every game: it deals and rolls, checks each move against the rules, runs the turn clocks, and sends each player only what that player may see.

The routes on this page are user-only. A bot or OAuth2 bearer credential is refused with 403 ACCESS_DENIED. Every route under a channel needs the access Get channel needs on a guild voice channel. Opening a table, sitting down, watching, and reading also need CONNECT and a connection_id that passes the voice membership check.

ValueDescription
okeyClassic Okey for four players, one hand
okey101101 Okey for four players, one hand, scored at its end
chessChess for two players, at positions 1 and 3
damaTürk daması for two players, at positions 1 and 3
dama_normalNormal dama, Brazilian draughts on the dark squares, for two players at positions 1 and 3
tavlaTavla, backgammon with no doubling cube, one game for two players at positions 1 and 3

The player who opens a table chooses its speed.

ValueDescription
fast15 seconds a turn in okey, 25 in 101, and 20 in tavla, and 3 minutes a side in chess and both damas
normal30 seconds a turn in okey, 45 in 101, and 40 in tavla, and 10 minutes a side in chess and both damas
slow60 seconds a turn in okey, 90 in 101, and 90 in tavla, and 30 minutes a side in chess and both damas

A chess or dama clock adds no increment.

ValueDescription
lobbyThe table is filling, and no game runs yet
playingThe game runs
endedThe game is over, and result says how
  • Open a table game puts the caller at position 1 of a lobby. Okey and 101 fill positions 1 to 4, and chess, both damas, and tavla fill positions 1 and 3.
  • The game starts as the last position is taken. The server shuffles and rolls with a cryptographic random source, and in chess, dama, and tavla it draws the position that plays white.
  • A lobby that does not fill within 10 minutes ends as canceled with reason abandoned, and so does a lobby its last player leaves. Leaving a lobby, or leaving the voice channel while in one, only gives up the position.
  • When a game ends, its players return to lounge seats, or stand when none is free.
  • An ended table stays readable for 5 minutes, and a new table can be opened on its slot at once.
  • An account plays at one open table at a time, in any channel.
  • A table whose slot is no longer in the channel’s room theme ends as canceled with reason theme_changed on the next move or tick at it.

Every change to a table raises its version by one and sends a Table Game Update Dispatch to its players and subscribed spectators.

In okey and 101 the turn passes from position 1 through 4 and around again, and deadline_at is the moment the turn runs out. The server then plays the turn for the player: it draws from the pile when the turn has not drawn yet, and discards the tile the player received last, or another one when that tile is a joker or would carry a penalty. A player whose turn runs out twice in a row loses the game as forfeit with reason timeouts. Any move of the player’s own resets that count.

In tavla each turn has its own time as well, and deadline_at is the moment it runs out. The server then plays legal_moves[0] for the player, and a player whose turn runs out twice in a row loses as forfeit with reason timeouts. Any move of the player’s own resets that count. A turn that passes because the roll has no play neither counts nor resets it.

In chess and both damas each side has its own clock. The side whose clock reaches zero loses, with reason timeout, whatever is left on the board.

The server settles an expired clock by itself on a timer that runs every second. A player’s client can also send a tick action, which settles whatever is due at once. A spectator cannot send one.

  • Leave a table game during a game loses it at once, as forfeit with reason leave.
  • A player who leaves the voice channel during a game is reported with connected false. Coming back within 30 seconds clears the mark. Otherwise the game ends as forfeit with reason disconnect, after the server confirms once more that the player is gone. When several players are gone, the one who left first forfeits. Only when every player of the game is out of the voice channel and they all left within 5 seconds of each other does nobody forfeit: the game ends as canceled with reason abandoned.
  • A player whose other connection is still in the channel is not disconnected.
  • The server also checks the players of a running game against the voice channel every 60 seconds, and marks a missing player the same way.

Anyone connected to the channel can watch a table they do not play at. Watch a table game subscribes the caller to the table’s Table Game Update Dispatches while the table has fewer than 50 subscribed spectators. Past that, subscribed is false, and the caller polls Get a table game with its ETag instead.

Watching also issues a watch ticket for 60 seconds, subscribed or not. While the ticket lasts, Get a table game needs no connection_id. After it expires, the request sends connection_id again, and one that passes the voice membership check issues a new ticket.

An account watches one table at a time, so watching a table stops watching the previous one. Leaving the voice channel stops watching its tables.

A watch seat beside a table is only a place to sit. Its holder watches through Watch a table game like anyone else, receives the public view with rack null, and has no other right at the table.

A player reads their own table at any time. Anyone else reads a table with a watch ticket or a connection_id, and List table games always needs the connection_id.

Both reads answer with an ETag, Cache-Control: private, no-cache, and Vary: Authorization, and a matching If-None-Match answers 304 with no body. A response can carry the caller’s own rack, so no shared cache may keep it.

The ETag of a table follows its version and the caller’s position, so a change of spectator_count alone answers 304. The ETag of the list covers the whole response. Treat both as opaque values.

The player who opens a table can make it ranked. A ranked table takes any game kind except tavla, and its finished games are rated for its players, one game kind at a time. A canceled game is never rated, and the server leaves a game unrated for a player when it ends too early or repeats a pairing too often.

The feature_table_game_ranked limit key switches ranked tables for the whole instance. While it is 0, opening a ranked table, the leaderboard routes and the reward showcase route return 403 FEATURE_TEMPORARILY_DISABLED, and profiles carry no leagues. Stored ratings stay.

A seat at a ranked table needs an account at least 7 days old, a verified email, and no account limitation. Opening or joining a ranked table without them returns 403 TABLE_GAME_RANKED_NOT_ELIGIBLE, with the failed check as reason.

A player of a running ranked game cannot end it by changing the channel’s room theme or deleting the channel: Modify channel and Delete or leave channel return 409 TABLE_GAME_RANKED_IN_PROGRESS when the change would take the game’s slot away. When anyone else makes that change, the ranked game ends at once as canceled with reason theme_changed, once the channel is written. A canceled ranked game stays unrated either way.

A player of a running ranked game who removes another player of that game from it, by disconnecting or moving them out of voice, kicking or banning them, loses the game at once as forfeit with reason leave. The owner deleting the guild loses their own running ranked game the same way, and every other running ranked game in the guild ends as canceled with reason theme_changed. The moderation action itself always goes through.

Each game kind keeps its own rating, which no response carries. A player’s first 5 rated games of a kind are placement games, and after them the player has a league in that kind. Below the legend league, progress is the way from the floor of the league to the next one, from 0 to 1. A player in the legend league has rr instead, the rank rating that orders the legend league.

A season is a calendar month in the Europe/Istanbul time zone, written as YYYYMM. The global leaderboard of a season lists a player in a game kind after 20 rated games of that kind in the season.

Get a table game leaderboard reads the instance-wide leaderboard of one game kind. It lists only players who show on leaderboards: players with a recorded date of birth of 16 years or older who have not set table_game_leaderboard_hidden in their user settings. Pages are shared between callers for 30 seconds, so a change can take that long to show.

Get a guild table game leaderboard reads the weekly points of a guild’s members. Rated wins and draws of ranked games at the guild’s tables earn points. A week starts on Monday 00:00 in the Europe/Istanbul time zone, and a member is listed after 5 games in the guild that week.

Get table game rewards reads the current season’s reward showcase, configured by staff for five global rank tiers. It does not describe last season’s frozen rewards or guild weekly rewards.

Both leaderboards serve their first 1000 places. A leaderboard the server has not built yet answers with no entries while it is rebuilt.

A tile is a string from t0 to t105. For a numbered tile tN, the color is (N mod 52) div 13 and the number is (N mod 13) + 1, and every numbered face exists twice.

ValueDescription
t0 to t103A numbered tile
t104, t105The two false jokers

The color values are these.

ValueDescription
0Yellow
1Blue
2Black
3Red

The indicator decides the joker: the face of the indicator’s color with the next number, where 1 follows 13. The two tiles of that face are jokers, and the false jokers stand for that face.

Squares are named as in chess, a1 to h8, and white starts on the low ranks and moves toward rank 8. White moves first in both damas. The two kinds differ in these rules.

RuleTürk daması (dama)Normal dama (dama_normal)
BoardAll 64 squares, 16 men a side on ranks 2-3 and 6-7The 32 dark squares with a1 dark, 12 men a side on ranks 1-3 and 6-8
Men moveOne square forward or sidewaysOne square diagonally forward
Men captureForward or sidewaysDiagonally, forward or backward
KingsFly any distance along ranks and filesFly any distance along diagonals
Taken piecesLeave the board at once, so their squares can be crossed againStay as blockers until the move ends, and none is jumped twice
Lone kingsOne king against one king is a draw at oncePlay goes on

A dama_move names the whole move as path: the square the piece starts on, then every square it stops on, 2 to 17 squares. A plain move is two squares. A capture lists one landing square for each piece it takes, so a chain that takes three pieces is four squares in one request.

  • Capturing is compulsory, and the move takes as many pieces as the most any capture on the board takes. Men and kings count the same.
  • A king captures at a distance and lands on any empty square past the piece it takes.
  • No piece turns back the way it came within a capture.
  • A man that reaches the last rank during a capture goes on as a man, and becomes a king only when the move ends there.
  • Routes from the same start to the same end that take the same pieces are one move. legal_moves lists one of them, and the server accepts any of them.

The server checks each leg of a path against the rules, not against the list. legal_moves holds at most 1024 paths, and a path a crowded position leaves off the list is still accepted.

A side left with no pieces, or with no legal move on its turn, loses. The game is drawn when the same position with the same side to move comes up three times since the last advance, or after 25 moves by each side with no advance. An advance is a capture or a man moving forward, so a sideways man move or any king move in Türk daması is not one.

Points are numbered 1 to 24 in one frame for both sides, in points, tavla_move, last_moves, and legal_moves. White moves from 24 down to 1 and bears off from its home, points 1 to 6. Black moves from 1 up to 24 and bears off from points 19 to 24. Each side starts with 15 checkers: white has two on point 24, five on 13, three on 8, and five on 6, and black mirrors that on points 1, 12, 17, and 19.

A checker entering from the bar on die d lands on point 25 - d for white and on point d for black. bar is only ever a from, and off only ever a to.

The server rolls the dice itself, from a cryptographic random source, as each turn starts. There is no roll action, and no roll exists before its turn starts. At the start of the game the server rolls one die for each side, rerolling ties, and the side with the higher die starts with two fresh dice. opening shows that opening roll on the first turn only.

A tavla_move carries the whole turn in one request, 1 to 4 steps.

  • Both dice are played when possible. When only one of them can be played, it is the larger one if either could be. Doubles give four steps, played as far as they go.
  • A checker on the bar enters before any other checker moves.
  • A lone checker that is hit goes to the bar.
  • Bearing off needs all 15 of the side’s checkers in its home. A die higher than the farthest checker takes that checker off, and a die that fits a checker exactly may still be played inside the home instead.
  • The server checks the turn as a whole, so any order or split of a legal play is accepted, such as a 6-5 sent as 24/18 18/13 or as 24/19 19/13.

legal_moves holds one string for each distinct board a turn can leave, at most 3060. A string is the steps as from/to joined by spaces, such as bar/22 13/7 or 6/off, farthest from the mover’s home first: the bar, then points descending for white and ascending for black. legal_moves[0] is the play the server makes when the turn runs out.

When a roll has no play, the turn passes at once and the next player’s dice are rolled in the same change. An empty legal_moves shows up only after more than 16 such passes in a row, and that turn then passes when it runs out, without counting a timeout.

The first side to bear off all 15 checkers wins with reason bear_off, and the result has finish mars when the loser bore off none. A resignation or a forfeit is never a mars.

FieldTypeDescription
positionintegerThe position of the player (1-4)
user_idsnowflakeThe ID of the player
connectedbooleanFalse while the player’s disconnect grace runs
timeoutsintegerThe turns in a row the player let run out, always 0 in chess and both damas

The kind field decides the other fields.

FieldTypeDescription
kindstringwin, forfeit, draw, or canceled
winners?1array[integer]The winning positions
loser?2integerThe position that lost
reasonstringThe result reason
finish?3stringHow the game was finished, one of normal, pairs, okey, hand, and mars

1 Present for win. A 101 hand can have several winners on equal scores

2 Present for forfeit

3 Present on an okey win by finishing, on a 101 win when a player finished the hand, and on a tavla win when the loser bore off no checker. pairs is okey only, hand is 101 only, and mars is tavla only

ValueDescription
finishWin: a player finished the okey hand
scoreWin: the lowest 101 hand score, after a finish or an empty pile
checkmateWin: checkmate
timeoutWin: the other side’s chess or dama clock ran out
resignWin: the other side resigned
bear_offWin: a tavla player bore off all 15 checkers
no_piecesWin: the other dama side has no pieces left
blockedWin: the other dama side has no legal move on its turn
leaveForfeit: the player left the table
disconnectForfeit: the player stayed out of the voice channel past the grace
timeoutsForfeit: the player’s turn ran out twice in a row
pile_emptyDraw: the okey pile ran out
stalemateDraw: stalemate
insufficientDraw: neither side can checkmate
threefoldDraw: the same position came up three times
fifty_moveDraw: fifty moves by each side with no capture or pawn move
move_limitDraw: 25 dama moves by each side with no capture and no man moving forward
lone_kingsDraw: one king against one king and nothing else, in Türk daması
disabledCanceled: table games were switched off, or the rollout no longer reaches a player
theme_changedCanceled: the slot left the channel’s room theme
abandonedCanceled: the lobby did not fill, its last player left, or every player left voice within 5 seconds of each other
FieldTypeDescription
slotstringThe table slot, such as T01
gamestringThe game kind
speedstringThe game speed
statusstringThe table status
playersarray[table game player object]The seated players, by position
spectator_countintegerThe subscribed spectators
versionintegerThe table version, raised by one on every change
result?table game result objectHow the game ended, null until it ends
ranked?booleanPresent and true only at a ranked table

The game view of Classic Okey. Each four-element array is indexed by position, so its first element belongs to position 1.

FieldTypeDescription
gamestringAlways okey
indicatorstringThe indicator tile, which decides the joker
pile_countintegerThe tiles left in the pile
discardsarray[array[string]]The tiles each position discarded, oldest first
rack_countsarray[integer]The number of tiles in each rack
turn?integerThe position whose turn it is, null once the game ended
phasestringWhat the player on turn does next, draw or discard
deadline_at?ISO8601 timestampWhen the turn runs out, null once the game ended
rack1?array[string]The viewer’s own rack, null for everyone else

1 A player receives their own rack in their own responses and Dispatches only

A player draws from the pile or takes the newest discard of the previous position.

The game view of 101 Okey has every okey view field, with game always okey101 and phase either draw or play, and these fields besides.

FieldTypeDescription
meldsarray[okey 101 meld object]The melds laid on the table
openedarray[string]How each position opened, one of none, sets, and pairs
penaltiesarray[integer]The penalty points of each position
scores?array[integer]The hand score of each position, null until the hand is over
FieldTypeDescription
idintegerThe ID of the meld, which an addition names as meld_id
ownerintegerThe position that laid it
kindstringrun, group, or pair
tilesarray[string]Its tiles in order
FieldTypeDescription
gamestringAlways chess
fenstringThe position in Forsyth-Edwards Notation
whiteintegerThe position that plays white, 1 or 3
clocks_msobjectwhite and black, the milliseconds each side had left when the current turn started
turn_started_at?ISO8601 timestampWhen the side to move started its turn, null once the game ended
last_move?stringThe last move in UCI notation, such as e2e4 or e7e8q
legal_moves?array[string]The legal moves in UCI notation, for the player to move alone and null for everyone else
in_checkbooleanWhether the side to move is in check

The game view of both damas, the same for every viewer apart from legal_moves.

FieldTypeDescription
gamestringdama or dama_normal
boardstringThe 64 squares a8 to h8, a7 to h7, and on down to h1: . empty, w and b men, W and B kings
whiteintegerThe position that plays white, 1 or 3
turn?integerThe position to move, null once the game ended
clocks_msobjectwhite and black, the milliseconds each side had left when the current turn started
turn_started_at?ISO8601 timestampWhen the side to move started its turn, null once the game ended
last_move?array[string]The path of the last move, start first, null before the first move
last_capturedarray[string]The squares of the pieces the last move took, in capture order
legal_moves?array[string]The legal paths with squares joined by -, such as c3-c5-e5, for the player to move alone

legal_moves is null for everyone but the player to move, and holds at most 1024 paths.

The game view of tavla, the same for every viewer apart from legal_moves. Point numbers follow Tavla turns.

FieldTypeDescription
gamestringAlways tavla
whiteintegerThe position that plays white, 1 or 3
pointsarray[integer]24 checker counts, the first for point 1: positive for white, negative for black
barobjectwhite and black, the checkers each side has on the bar
offobjectwhite and black, the checkers each side has borne off
turn?integerThe position whose turn it is, null once the game ended
dice?array[integer]The two dice of the current turn, null once the game ended
opening?array[integer]The opening roll as white’s die and black’s die, on the first turn only and otherwise null
deadline_at?ISO8601 timestampWhen the turn runs out, null once the game ended
last_movesarray[tavla step object]The steps of the last turn played, each with hit, empty before the first
legal_moves?array[string]Each distinct way to play the turn, such as 24/18 13/7, for the player on turn alone

legal_moves is null for everyone but the player on turn, and holds at most 3060 plays.

FieldTypeDescription
frominteger or stringThe point the checker leaves (1-24), or bar
tointeger or stringThe point the checker reaches (1-24), or off when it is borne off
hit?1booleanWhether the step hit a lone checker of the other side to the bar

1 Present in last_moves only, and never sent in a tavla_move

A table as one caller may see it.

FieldTypeDescription
summarytable game summary objectThe public summary of the table
game_view?objectThe okey, 101, chess, dama, or tavla view, null in the lobby
viewer_position?integerThe caller’s position at the table, null when the caller does not play at it
spectatingbooleanWhether the caller is a subscribed spectator

The view of the player at position 2, whose turn it is to discard. Only that player’s own rack is present.

{
"summary": {
"slot": "T01",
"game": "okey",
"speed": "normal",
"status": "playing",
"players": [
{"position": 1, "user_id": "1501314428688998101", "connected": true, "timeouts": 0},
{"position": 2, "user_id": "1501314428688998102", "connected": true, "timeouts": 0},
{"position": 3, "user_id": "1501314428688998103", "connected": false, "timeouts": 0},
{"position": 4, "user_id": "1501314428688998104", "connected": true, "timeouts": 1}
],
"spectator_count": 3,
"version": 12,
"result": null
},
"game_view": {
"game": "okey",
"indicator": "t17",
"pile_count": 43,
"discards": [["t3", "t70"], ["t60"], ["t91"], ["t40"]],
"rack_counts": [14, 15, 14, 14],
"turn": 2,
"phase": "discard",
"deadline_at": "2026-10-03T07:12:30.000Z",
"rack": ["t0", "t5", "t12", "t14", "t22", "t27", "t33", "t48", "t53", "t66", "t79", "t84", "t97", "t101", "t104"]
},
"viewer_position": 2,
"spectating": false
}
ValueDescription
tickSettles whatever is due at the table, in any game, from a player only
drawDraws a tile in okey from pile or discard, and in 101 from pile only
discardDiscards tile in okey or 101
finishFinishes an okey hand, throwing tile and showing the rest of the rack as sets
take_discardTakes the previous position’s newest discard in 101, and uses it at once in open or add
meldOpens or adds to melds in 101, through open and add
moveMoves a chess piece from from to to, with promotion for a pawn reaching the last rank
dama_movePlays a whole dama move along path, every capture of a chain in one request
tavla_movePlays a whole tavla turn as moves
resignResigns a chess, dama, or tavla game

The type field decides the other fields.

FieldTypeDescription
typestringThe action type
from?stringpile or discard for draw, and a square such as e2 for move
to?stringThe square a chess piece moves to
promotion?stringThe piece a pawn becomes, one of q, r, b, and n
tile?stringThe tile to discard, or for finish the tile thrown to end the hand
sets?array[array[string]]For finish, the rest of the rack as 1-7 melds of 2-13 tiles each
open?array[array[string]]For take_discard and meld, up to 12 new melds of 2-13 tiles each
add?array[okey addition object]For take_discard and meld, up to 12 additions to melds on the table
path?array[string]For dama_move, the start square and every square the piece stops on, 2-17 squares
moves?array[tavla step object]For tavla_move, the steps of the turn (1-4), each from and to
FieldTypeDescription
meld_idintegerThe meld to add to (0-255)
tilesarray[string]The tiles to add (1-12)
atstringThe end of the meld the tiles join, start or end

A move the rules refuse returns 400 TABLE_GAME_RULE_VIOLATION, and the error response adds a reason member from this closed set. The body names no tile.

ValueDescription
not_your_turnIt is another position’s turn
wrong_phaseThe action does not fit the phase of the turn
tile_not_in_rackA named tile is not in the caller’s rack
invalid_meldA set, run, group, or pair is not valid
open_too_lowA 101 opening with runs and groups counts too few points
pairs_too_fewA 101 opening with pairs has too few pairs
take_unusedA discard taken in 101 is not used in the same action
not_openedA 101 player who has not opened adds to a meld
cannot_addA tile does not fit the meld it is added to, or adding is not allowed yet
illegal_moveThe chess, dama, or tavla move is not legal
game_overThe game has already ended
unsupported_actionThe game has no such action
must_finish_with_discardA 101 meld would leave no tile to discard
ValueDescription
bronzeThe first league, below silver
silverThe second league
goldThe third league
diamondThe fourth league
masterThe fifth league, below legend
legendThe top league, ordered by rr
FieldTypeDescription
rankintegerThe place on the leaderboard, from 1
user_idsnowflakeThe ID of the player
user?partial user objectThe player, or null when the account cannot be read
leaguestringThe player’s league
progress?numberThe progress toward the next league (0-1), null in the legend league
rr?integerThe rank rating in the legend league, null below it
FieldTypeDescription
rankintegerThe place on the leaderboard, from 1
user_idsnowflakeThe ID of the member
user?partial user objectThe member, or null when the account cannot be read
pointsintegerThe member’s points in the guild that week
gamesintegerThe member’s rated games in the guild that week
winsintegerThe member’s rated wins in the guild that week
FieldTypeDescription
gamestringThe game kind
league?stringThe caller’s league, null while placement games remain
placement_leftintegerThe placement games still to play
progress?numberThe progress toward the next league (0-1), null in placement and in the legend league
rr?integerThe rank rating in the legend league, null below it
rank?integerThe place on this season’s global leaderboard, null when the caller is not on it
seasonintegerThe current season as YYYYMM
season_gamesintegerThe caller’s rated games of this kind in the season
qualifiedbooleanWhether the season games qualify the caller for the global leaderboard
gamesintegerThe caller’s rated games of this kind
winsintegerThe caller’s rated wins of this kind
lossesintegerThe caller’s rated losses of this kind
drawsintegerThe caller’s rated draws of this kind
FieldTypeDescription
gamestringThe game kind
leaguestringThe player’s league
rr?integerThe rank rating in the legend league, null below it
rank?integerThe place on this season’s global leaderboard in the legend league, null below it

The type field decides the other field. Unavailable rewards are omitted.

FieldTypeDescription
typestringpremium_days, cosmetic, badge or offsite
days1integerThe Micup Plus days granted
item2cosmetic item objectThe avatar decoration, with its name and asset
badge3badge objectThe badge, with its icon and description
description4stringWhat staff hand over by hand

1 Present for premium_days

2 Present for cosmetic

3 Present for badge

4 Present for offsite

FieldTypeDescription
title?stringThe heading set by staff, null to use the client’s translated default
chance_rewards_enabledbooleanWhether okey and okey 101 receive Micup Plus rewards; clients hide premium_days for those games while false
rank1array[table game showcase reward object]First-place rewards
rank2array[table game showcase reward object]Second-place rewards
rank3array[table game showcase reward object]Third-place rewards
top10array[table game showcase reward object]Rewards for places 4 through 10
top100array[table game showcase reward object]Rewards for places 11 through 100

Every route under a channel can also answer with these.

StatusBodyCondition
400error responseThe channel is not a guild voice channel, returned as INVALID_CHANNEL_TYPE
401error responseNo account is signed in
403error responseThe credential is a bot or OAuth2 bearer token, returned as ACCESS_DENIED
403error responseTable games are switched off, returned as FEATURE_TEMPORARILY_DISABLED
403error responseThe caller lacks a permission the route needs, returned as MISSING_PERMISSIONS
404error responseThe channel does not exist, returned as UNKNOWN_CHANNEL
GET/v1/channels/{channel_id}/table-games

Returns the summary of every table at a slot of the channel’s room theme, in the theme’s slot order. A slot with no table is left out, and an ended table is listed for the 5 minutes it stays readable.

FieldTypeDescription
channel_idsnowflakeThe ID of the voice channel
FieldTypeDescription
connection_idstringThe caller’s voice connection in this channel (1-64 characters)
FieldTypeDescription
If-None-Match?stringAn ETag from an earlier response to this route
FieldTypeDescription
tablesarray[table game summary object]The tables of the room
FieldTypeDescription
ETagstringA strong tag over the response body, sent on 200 and on 304
Cache-ControlstringThe literal value private, no-cache
VarystringThe literal value Authorization, replaced by Origin where the cross-origin policy echoed an allowed origin
StatusBodyCondition
200response bodyThe tables were listed
304emptyThe request sent a matching If-None-Match
400error responseconnection_id is absent or fails the membership check, returned as USER_NOT_IN_VOICE

The common responses also apply.

60 requests per 10 seconds for each authenticated user and channel ID, on the shared table_game:read::channel_id bucket.

GET/v1/channels/{channel_id}/table-games/{slot}

Returns the table game view of one table. A player at the table receives their own rack, and anyone else the public view. A caller who does not play at the table needs a valid watch ticket or a connection_id that passes the membership check, and a request that passes it issues a new ticket.

FieldTypeDescription
channel_idsnowflakeThe ID of the voice channel
slotstringThe table slot, T and two digits such as T01
FieldTypeDescription
connection_id?stringThe caller’s voice connection in this channel (1-64 characters)
FieldTypeDescription
If-None-Match?stringAn ETag from an earlier response to this route
FieldTypeDescription
ETagstringA strong tag of the table version and the caller’s position, sent on 200 and on 304
Cache-ControlstringThe literal value private, no-cache
VarystringThe literal value Authorization, replaced by Origin where the cross-origin policy echoed an allowed origin
StatusBodyCondition
200table game view objectThe table was returned
304emptyThe request sent a matching If-None-Match
400error responseA caller with no ticket sent no connection_id or one that fails the check, returned as USER_NOT_IN_VOICE
404error responseThe slot has no table, returned as UNKNOWN_TABLE_GAME

The common responses also apply.

60 requests per 10 seconds for each authenticated user and channel ID, on the shared table_game:read::channel_id bucket.

POST/v1/channels/{channel_id}/table-games/{slot}

Opens a lobby at a table slot, seats the caller at position 1, and returns the caller’s table game view. A table that has ended is replaced. The lobby ends as canceled with reason abandoned when it does not fill within 10 minutes.

FieldTypeDescription
channel_idsnowflakeThe ID of the voice channel
slotstringA table slot of the channel’s room theme
FieldTypeDescription
gamestringThe game kind
speedstringThe game speed
connection_idstringThe caller’s voice connection in this channel (1-64 characters)
ranked?booleanWhether the table is ranked, false by default
StatusBodyCondition
201table game view objectThe lobby was opened
400error responseconnection_id fails the membership check, returned as USER_NOT_IN_VOICE
400error responseranked is true for tavla, returned as INVALID_FORM_BODY
403error responseranked is true while ranked tables are off, returned as FEATURE_TEMPORARILY_DISABLED
403error responseranked is true and the account fails a seat check, as TABLE_GAME_RANKED_NOT_ELIGIBLE
404error responseThe slot is not in the channel’s room theme, returned as UNKNOWN_TABLE_GAME
409error responseThe slot has an open table, or the caller plays at another one, returned as TABLE_GAME_CONFLICT

The common responses also apply.

The caller’s connection moves to the table’s position 1 seat, and the table’s players receive a Table Game Update Dispatch.

5 requests per minute for each authenticated user and channel ID, on the table_game:create::channel_id bucket.

PUT/v1/channels/{channel_id}/table-games/{slot}/players/@me

Sits the caller down at a free position of a lobby and returns the caller’s table game view. Taking the last free position starts the game.

FieldTypeDescription
channel_idsnowflakeThe ID of the voice channel
slotstringA table slot of the channel’s room theme
FieldTypeDescription
positionintegerThe position to take (1-4), where chess, both damas, and tavla use 1 and 3 only
connection_idstringThe caller’s voice connection in this channel (1-64 characters)
StatusBodyCondition
200table game view objectThe caller sat down
400error responseconnection_id fails the membership check, returned as USER_NOT_IN_VOICE
403error responseThe table is ranked and the account fails a seat check, as TABLE_GAME_RANKED_NOT_ELIGIBLE
404error responseThe slot is not in the room theme or has no table, returned as UNKNOWN_TABLE_GAME
409error responseThe table is not a lobby, or the position is taken or unused by the game, as TABLE_GAME_CONFLICT
409error responseThe caller already plays at this or another table, returned as TABLE_GAME_CONFLICT

The common responses also apply.

The caller’s connection moves to the position’s seat, and the table’s players and spectators receive a Table Game Update Dispatch.

10 requests per 30 seconds for each authenticated user, channel ID, and table slot, on the shared table_game:seat::channel_id::slot bucket.

DELETE/v1/channels/{channel_id}/table-games/{slot}/players/@me

Gives up the caller’s position in a lobby, or loses a running game at once as forfeit with reason leave, and returns 204 with an empty body. Leaving an ended table changes nothing.

FieldTypeDescription
channel_idsnowflakeThe ID of the voice channel
slotstringThe table slot
StatusBodyCondition
204emptyThe caller left the table
404error responseThe slot has no table, returned as UNKNOWN_TABLE_GAME
409error responseThe caller does not play at the table, returned as TABLE_GAME_CONFLICT

The common responses also apply.

The caller returns to a lounge seat, or stands when none is free. The players, the subscribed spectators, and the caller receive a Table Game Update Dispatch.

10 requests per 30 seconds for each authenticated user, channel ID, and table slot, on the shared table_game:seat::channel_id::slot bucket.

POST/v1/channels/{channel_id}/table-games/{slot}/actions

Applies a player’s action to the table and returns the caller’s table game view. The server first settles whatever is due at the table, such as an expired turn, and then judges the action against the settled table.

FieldTypeDescription
channel_idsnowflakeThe ID of the voice channel
slotstringThe table slot
FieldTypeDescription
versionintegerThe table version the action was made against, ignored for tick
actiontable game action objectThe action

A version other than the table’s current one returns 409, and the client reads the table again. When the clock ran out before the action arrived, the settled turn is written and the action is then refused, so a refused action can still change the table.

StatusBodyCondition
200table game view objectThe action was applied
400error responseThe rules refuse the action, returned as TABLE_GAME_RULE_VIOLATION with a reason
404error responseThe slot has no table, returned as UNKNOWN_TABLE_GAME
409error responseThe caller does not play at the table, which includes every spectator, as TABLE_GAME_CONFLICT
409error responseThe version is stale, or a move is sent to a lobby, returned as TABLE_GAME_CONFLICT

The common responses also apply.

An action that changes the table sends a Table Game Update Dispatch to its players and subscribed spectators before the response.

30 requests per 10 seconds for each authenticated user, channel ID, and table slot, on the table_game:action::channel_id::slot bucket.

PUT/v1/channels/{channel_id}/table-games/{slot}/spectators/@me

Subscribes the caller to the table’s updates while it has fewer than 50 subscribed spectators, issues a 60 second watch ticket either way, and returns whether the caller is subscribed with the public view.

FieldTypeDescription
channel_idsnowflakeThe ID of the voice channel
slotstringThe table slot
FieldTypeDescription
connection_idstringThe caller’s voice connection in this channel (1-64 characters)
FieldTypeDescription
subscribedbooleanWhether the caller receives the table’s Dispatches, false while the spectators are full
viewtable game view objectThe public view of the table
StatusBodyCondition
200response bodyThe caller watches the table
400error responseconnection_id fails the membership check, returned as USER_NOT_IN_VOICE
404error responseThe slot has no table, returned as UNKNOWN_TABLE_GAME
409error responseThe caller plays at the table, returned as TABLE_GAME_CONFLICT

The common responses also apply.

The caller stops watching any other table, subscribed or not.

10 requests per minute for each authenticated user and channel ID, on the shared table_game:spectate::channel_id bucket.

DELETE/v1/channels/{channel_id}/table-games/{slot}/spectators/@me

Ends the caller’s subscription to the table’s updates and returns 204 with an empty body, whether or not the caller was subscribed.

FieldTypeDescription
channel_idsnowflakeThe ID of the voice channel
slotstringThe table slot
StatusBodyCondition
204emptyThe caller no longer watches the table

The common responses also apply.

10 requests per minute for each authenticated user and channel ID, on the shared table_game:spectate::channel_id bucket.

GET/v1/table-games/rewards

Returns the current reward showcase. Requires a signed-in user account; bot and OAuth2 credentials cannot use this route. The response contains no player data. An empty tier has no rewards to show. Catalog item changes can take up to 60 seconds to appear.

StatusBodyCondition
200table game reward showcase objectThe showcase was returned
401error responseNo account is signed in
403error responseThe credential is a bot or OAuth2 bearer token, returned as ACCESS_DENIED
403error responseRanked table games are switched off, returned as FEATURE_TEMPORARILY_DISABLED

20 requests per 10 seconds for each authenticated user, on the table_game:rewards bucket.

GET/v1/table-games/leaderboards/{game}

Returns a page of the instance-wide leaderboard of one game kind, this season or the last. Entries carry the league, the progress, and the legend league’s rr, never the rating.

FieldTypeDescription
gamestringA game kind other than tavla
FieldTypeDescription
season?stringcurrent or previous, current by default
offset?integerThe places to skip (0-950), 0 by default
limit?integerThe most entries to return (1-50), 50 by default
FieldTypeDescription
gamestringThe game kind
seasonintegerThe season as YYYYMM
totalintegerThe players on the leaderboard
entriesarray[table game leaderboard entry object]The page, in place order
StatusBodyCondition
200response bodyThe page was returned
400error responseA parameter is invalid, returned as INVALID_FORM_BODY
401error responseNo account is signed in
403error responseThe credential is a bot or OAuth2 bearer token, returned as ACCESS_DENIED
403error responseRanked tables are off, returned as FEATURE_TEMPORARILY_DISABLED

20 requests per 10 seconds for each authenticated user, on the table_game:leaderboard bucket.

GET/v1/guilds/{guild_id}/table-games/leaderboard

Returns a page of the weekly leaderboard of a guild, this week or the last, with the caller’s own standing. The caller must be a member of the guild.

FieldTypeDescription
guild_idsnowflakeThe ID of the guild
FieldTypeDescription
week?stringcurrent or previous, current by default
offset?integerThe places to skip (0-950), 0 by default
limit?integerThe most entries to return (1-50), 50 by default
FieldTypeDescription
weekintegerThe Monday that starts the week, as YYYYMMDD
starts_atISO8601 timestampThe start of the week
ends_atISO8601 timestampThe start of the next week
totalintegerThe members on the leaderboard
entriesarray[guild table game leaderboard entry object]The page, in place order
me?standing objectThe caller’s standing, null when the caller has no rated game there that week
FieldTypeDescription
pointsintegerThe caller’s points in the guild that week
gamesintegerThe caller’s rated games in the guild that week
rank?integerThe caller’s place, null when the caller is not on the leaderboard
StatusBodyCondition
200response bodyThe page was returned
400error responseA parameter is invalid, returned as INVALID_FORM_BODY
401error responseNo account is signed in
403error responseThe credential is a bot or OAuth2 bearer token, returned as ACCESS_DENIED
403error responseThe caller is not a member, or the guild does not exist, as MISSING_ACCESS
403error responseRanked tables are off, returned as FEATURE_TEMPORARILY_DISABLED

20 requests per 10 seconds for each authenticated user and guild ID, on the guild:table_game_leaderboard::guild_id bucket.

GET/v1/users/@me/table-game-ratings

Returns the caller’s ranked standing in each game kind the caller has a rated game in, and whether the caller shows on leaderboards. The response never carries the rating itself.

FieldTypeDescription
listedbooleanWhether the caller shows on leaderboards
ratingsarray[table game rating summary object]One summary per game kind, in game kind order
StatusBodyCondition
200response bodyThe standing was returned
401error responseNo account is signed in
403error responseThe credential is a bot or OAuth2 bearer token, returned as ACCESS_DENIED
403error responseRanked tables are off, returned as FEATURE_TEMPORARILY_DISABLED

30 requests per 10 seconds for each authenticated user, on the table_game:ratings:me bucket.