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.
Game kinds
Section titled “Game kinds”| Value | Description |
|---|---|
| okey | Classic Okey for four players, one hand |
| okey101 | 101 Okey for four players, one hand, scored at its end |
| chess | Chess for two players, at positions 1 and 3 |
| dama | Türk daması for two players, at positions 1 and 3 |
| dama_normal | Normal dama, Brazilian draughts on the dark squares, for two players at positions 1 and 3 |
| tavla | Tavla, backgammon with no doubling cube, one game for two players at positions 1 and 3 |
Game speeds
Section titled “Game speeds”The player who opens a table chooses its speed.
| Value | Description |
|---|---|
| fast | 15 seconds a turn in okey, 25 in 101, and 20 in tavla, and 3 minutes a side in chess and both damas |
| normal | 30 seconds a turn in okey, 45 in 101, and 40 in tavla, and 10 minutes a side in chess and both damas |
| slow | 60 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.
Table statuses
Section titled “Table statuses”| Value | Description |
|---|---|
| lobby | The table is filling, and no game runs yet |
| playing | The game runs |
| ended | The game is over, and result says how |
Table lifecycle
Section titled “Table lifecycle”- 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
canceledwith reasonabandoned, 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
canceledwith reasontheme_changedon 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.
Turns and clocks
Section titled “Turns and clocks”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.
Leaving and disconnects
Section titled “Leaving and disconnects”- Leave a table game during a game loses it at once, as
forfeitwith reasonleave. - A player who leaves the voice channel during a game is reported with
connectedfalse. Coming back within 30 seconds clears the mark. Otherwise the game ends asforfeitwith reasondisconnect, 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 ascanceledwith reasonabandoned. - 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.
Watching
Section titled “Watching”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.
Reading tables
Section titled “Reading tables”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.
Ranked tables
Section titled “Ranked tables”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.
Leagues and seasons
Section titled “Leagues and seasons”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.
Leaderboards
Section titled “Leaderboards”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.
Tile tokens
Section titled “Tile tokens”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.
| Value | Description |
|---|---|
| t0 to t103 | A numbered tile |
| t104, t105 | The two false jokers |
The color values are these.
| Value | Description |
|---|---|
| 0 | Yellow |
| 1 | Blue |
| 2 | Black |
| 3 | Red |
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.
Dama moves
Section titled “Dama moves”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.
| Rule | Türk daması (dama) | Normal dama (dama_normal) |
|---|---|---|
| Board | All 64 squares, 16 men a side on ranks 2-3 and 6-7 | The 32 dark squares with a1 dark, 12 men a side on ranks 1-3 and 6-8 |
| Men move | One square forward or sideways | One square diagonally forward |
| Men capture | Forward or sideways | Diagonally, forward or backward |
| Kings | Fly any distance along ranks and files | Fly any distance along diagonals |
| Taken pieces | Leave the board at once, so their squares can be crossed again | Stay as blockers until the move ends, and none is jumped twice |
| Lone kings | One king against one king is a draw at once | Play 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_moveslists 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.
Tavla turns
Section titled “Tavla turns”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/13or as24/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.
Table game player object
Section titled “Table game player object”| Field | Type | Description |
|---|---|---|
| position | integer | The position of the player (1-4) |
| user_id | snowflake | The ID of the player |
| connected | boolean | False while the player’s disconnect grace runs |
| timeouts | integer | The turns in a row the player let run out, always 0 in chess and both damas |
Table game result object
Section titled “Table game result object”The kind field decides the other fields.
| Field | Type | Description |
|---|---|---|
| kind | string | win, forfeit, draw, or canceled |
| winners?1 | array[integer] | The winning positions |
| loser?2 | integer | The position that lost |
| reason | string | The result reason |
| finish?3 | string | How 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
Table game result reasons
Section titled “Table game result reasons”| Value | Description |
|---|---|
| finish | Win: a player finished the okey hand |
| score | Win: the lowest 101 hand score, after a finish or an empty pile |
| checkmate | Win: checkmate |
| timeout | Win: the other side’s chess or dama clock ran out |
| resign | Win: the other side resigned |
| bear_off | Win: a tavla player bore off all 15 checkers |
| no_pieces | Win: the other dama side has no pieces left |
| blocked | Win: the other dama side has no legal move on its turn |
| leave | Forfeit: the player left the table |
| disconnect | Forfeit: the player stayed out of the voice channel past the grace |
| timeouts | Forfeit: the player’s turn ran out twice in a row |
| pile_empty | Draw: the okey pile ran out |
| stalemate | Draw: stalemate |
| insufficient | Draw: neither side can checkmate |
| threefold | Draw: the same position came up three times |
| fifty_move | Draw: fifty moves by each side with no capture or pawn move |
| move_limit | Draw: 25 dama moves by each side with no capture and no man moving forward |
| lone_kings | Draw: one king against one king and nothing else, in Türk daması |
| disabled | Canceled: table games were switched off, or the rollout no longer reaches a player |
| theme_changed | Canceled: the slot left the channel’s room theme |
| abandoned | Canceled: the lobby did not fill, its last player left, or every player left voice within 5 seconds of each other |
Table game summary object
Section titled “Table game summary object”| Field | Type | Description |
|---|---|---|
| slot | string | The table slot, such as T01 |
| game | string | The game kind |
| speed | string | The game speed |
| status | string | The table status |
| players | array[table game player object] | The seated players, by position |
| spectator_count | integer | The subscribed spectators |
| version | integer | The table version, raised by one on every change |
| result | ?table game result object | How the game ended, null until it ends |
| ranked? | boolean | Present and true only at a ranked table |
Okey view object
Section titled “Okey view object”The game view of Classic Okey. Each four-element array is indexed by position, so its first element belongs to position 1.
| Field | Type | Description |
|---|---|---|
| game | string | Always okey |
| indicator | string | The indicator tile, which decides the joker |
| pile_count | integer | The tiles left in the pile |
| discards | array[array[string]] | The tiles each position discarded, oldest first |
| rack_counts | array[integer] | The number of tiles in each rack |
| turn | ?integer | The position whose turn it is, null once the game ended |
| phase | string | What the player on turn does next, draw or discard |
| deadline_at | ?ISO8601 timestamp | When 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.
Okey 101 view object
Section titled “Okey 101 view object”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.
| Field | Type | Description |
|---|---|---|
| melds | array[okey 101 meld object] | The melds laid on the table |
| opened | array[string] | How each position opened, one of none, sets, and pairs |
| penalties | array[integer] | The penalty points of each position |
| scores | ?array[integer] | The hand score of each position, null until the hand is over |
Okey 101 meld object
Section titled “Okey 101 meld object”| Field | Type | Description |
|---|---|---|
| id | integer | The ID of the meld, which an addition names as meld_id |
| owner | integer | The position that laid it |
| kind | string | run, group, or pair |
| tiles | array[string] | Its tiles in order |
Chess view object
Section titled “Chess view object”| Field | Type | Description |
|---|---|---|
| game | string | Always chess |
| fen | string | The position in Forsyth-Edwards Notation |
| white | integer | The position that plays white, 1 or 3 |
| clocks_ms | object | white and black, the milliseconds each side had left when the current turn started |
| turn_started_at | ?ISO8601 timestamp | When the side to move started its turn, null once the game ended |
| last_move | ?string | The 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_check | boolean | Whether the side to move is in check |
Dama view object
Section titled “Dama view object”The game view of both damas, the same for every viewer apart from legal_moves.
| Field | Type | Description |
|---|---|---|
| game | string | dama or dama_normal |
| board | string | The 64 squares a8 to h8, a7 to h7, and on down to h1: . empty, w and b men, W and B kings |
| white | integer | The position that plays white, 1 or 3 |
| turn | ?integer | The position to move, null once the game ended |
| clocks_ms | object | white and black, the milliseconds each side had left when the current turn started |
| turn_started_at | ?ISO8601 timestamp | When 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_captured | array[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.
Tavla view object
Section titled “Tavla view object”The game view of tavla, the same for every viewer apart from legal_moves. Point numbers follow Tavla turns.
| Field | Type | Description |
|---|---|---|
| game | string | Always tavla |
| white | integer | The position that plays white, 1 or 3 |
| points | array[integer] | 24 checker counts, the first for point 1: positive for white, negative for black |
| bar | object | white and black, the checkers each side has on the bar |
| off | object | white and black, the checkers each side has borne off |
| turn | ?integer | The 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 timestamp | When the turn runs out, null once the game ended |
| last_moves | array[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.
Tavla step object
Section titled “Tavla step object”| Field | Type | Description |
|---|---|---|
| from | integer or string | The point the checker leaves (1-24), or bar |
| to | integer or string | The point the checker reaches (1-24), or off when it is borne off |
| hit?1 | boolean | Whether 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
Table game view object
Section titled “Table game view object”A table as one caller may see it.
| Field | Type | Description |
|---|---|---|
| summary | table game summary object | The public summary of the table |
| game_view | ?object | The okey, 101, chess, dama, or tavla view, null in the lobby |
| viewer_position | ?integer | The caller’s position at the table, null when the caller does not play at it |
| spectating | boolean | Whether the caller is a subscribed spectator |
Example
Section titled “Example”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}Table game action types
Section titled “Table game action types”| Value | Description |
|---|---|
| tick | Settles whatever is due at the table, in any game, from a player only |
| draw | Draws a tile in okey from pile or discard, and in 101 from pile only |
| discard | Discards tile in okey or 101 |
| finish | Finishes an okey hand, throwing tile and showing the rest of the rack as sets |
| take_discard | Takes the previous position’s newest discard in 101, and uses it at once in open or add |
| meld | Opens or adds to melds in 101, through open and add |
| move | Moves a chess piece from from to to, with promotion for a pawn reaching the last rank |
| dama_move | Plays a whole dama move along path, every capture of a chain in one request |
| tavla_move | Plays a whole tavla turn as moves |
| resign | Resigns a chess, dama, or tavla game |
Table game action object
Section titled “Table game action object”The type field decides the other fields.
| Field | Type | Description |
|---|---|---|
| type | string | The action type |
| from? | string | pile or discard for draw, and a square such as e2 for move |
| to? | string | The square a chess piece moves to |
| promotion? | string | The piece a pawn becomes, one of q, r, b, and n |
| tile? | string | The 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 |
Okey addition object
Section titled “Okey addition object”| Field | Type | Description |
|---|---|---|
| meld_id | integer | The meld to add to (0-255) |
| tiles | array[string] | The tiles to add (1-12) |
| at | string | The end of the meld the tiles join, start or end |
Rule violation reasons
Section titled “Rule violation reasons”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.
| Value | Description |
|---|---|
| not_your_turn | It is another position’s turn |
| wrong_phase | The action does not fit the phase of the turn |
| tile_not_in_rack | A named tile is not in the caller’s rack |
| invalid_meld | A set, run, group, or pair is not valid |
| open_too_low | A 101 opening with runs and groups counts too few points |
| pairs_too_few | A 101 opening with pairs has too few pairs |
| take_unused | A discard taken in 101 is not used in the same action |
| not_opened | A 101 player who has not opened adds to a meld |
| cannot_add | A tile does not fit the meld it is added to, or adding is not allowed yet |
| illegal_move | The chess, dama, or tavla move is not legal |
| game_over | The game has already ended |
| unsupported_action | The game has no such action |
| must_finish_with_discard | A 101 meld would leave no tile to discard |
Table game leagues
Section titled “Table game leagues”| Value | Description |
|---|---|
| bronze | The first league, below silver |
| silver | The second league |
| gold | The third league |
| diamond | The fourth league |
| master | The fifth league, below legend |
| legend | The top league, ordered by rr |
Table game leaderboard entry object
Section titled “Table game leaderboard entry object”| Field | Type | Description |
|---|---|---|
| rank | integer | The place on the leaderboard, from 1 |
| user_id | snowflake | The ID of the player |
| user | ?partial user object | The player, or null when the account cannot be read |
| league | string | The player’s league |
| progress | ?number | The progress toward the next league (0-1), null in the legend league |
| rr | ?integer | The rank rating in the legend league, null below it |
Guild table game leaderboard entry object
Section titled “Guild table game leaderboard entry object”| Field | Type | Description |
|---|---|---|
| rank | integer | The place on the leaderboard, from 1 |
| user_id | snowflake | The ID of the member |
| user | ?partial user object | The member, or null when the account cannot be read |
| points | integer | The member’s points in the guild that week |
| games | integer | The member’s rated games in the guild that week |
| wins | integer | The member’s rated wins in the guild that week |
Table game rating summary object
Section titled “Table game rating summary object”| Field | Type | Description |
|---|---|---|
| game | string | The game kind |
| league | ?string | The caller’s league, null while placement games remain |
| placement_left | integer | The placement games still to play |
| progress | ?number | The progress toward the next league (0-1), null in placement and in the legend league |
| rr | ?integer | The rank rating in the legend league, null below it |
| rank | ?integer | The place on this season’s global leaderboard, null when the caller is not on it |
| season | integer | The current season as YYYYMM |
| season_games | integer | The caller’s rated games of this kind in the season |
| qualified | boolean | Whether the season games qualify the caller for the global leaderboard |
| games | integer | The caller’s rated games of this kind |
| wins | integer | The caller’s rated wins of this kind |
| losses | integer | The caller’s rated losses of this kind |
| draws | integer | The caller’s rated draws of this kind |
Table game profile league object
Section titled “Table game profile league object”| Field | Type | Description |
|---|---|---|
| game | string | The game kind |
| league | string | The player’s league |
| rr | ?integer | The rank rating in the legend league, null below it |
| rank | ?integer | The place on this season’s global leaderboard in the legend league, null below it |
Table game showcase reward object
Section titled “Table game showcase reward object”The type field decides the other field. Unavailable rewards are omitted.
| Field | Type | Description |
|---|---|---|
| type | string | premium_days, cosmetic, badge or offsite |
| days1 | integer | The Micup Plus days granted |
| item2 | cosmetic item object | The avatar decoration, with its name and asset |
| badge3 | badge object | The badge, with its icon and description |
| description4 | string | What staff hand over by hand |
1 Present for premium_days
2 Present for cosmetic
3 Present for badge
4 Present for offsite
Table game reward showcase object
Section titled “Table game reward showcase object”| Field | Type | Description |
|---|---|---|
| title | ?string | The heading set by staff, null to use the client’s translated default |
| chance_rewards_enabled | boolean | Whether okey and okey 101 receive Micup Plus rewards; clients hide premium_days for those games while false |
| rank1 | array[table game showcase reward object] | First-place rewards |
| rank2 | array[table game showcase reward object] | Second-place rewards |
| rank3 | array[table game showcase reward object] | Third-place rewards |
| top10 | array[table game showcase reward object] | Rewards for places 4 through 10 |
| top100 | array[table game showcase reward object] | Rewards for places 11 through 100 |
Common responses
Section titled “Common responses”Every route under a channel can also answer with these.
| Status | Body | Condition |
|---|---|---|
| 400 | error response | The channel is not a guild voice channel, returned as INVALID_CHANNEL_TYPE |
| 401 | error response | No account is signed in |
| 403 | error response | The credential is a bot or OAuth2 bearer token, returned as ACCESS_DENIED |
| 403 | error response | Table games are switched off, returned as FEATURE_TEMPORARILY_DISABLED |
| 403 | error response | The caller lacks a permission the route needs, returned as MISSING_PERMISSIONS |
| 404 | error response | The channel does not exist, returned as UNKNOWN_CHANNEL |
List table games
Section titled “List table games”GET/v1/channels/{channel_id}/table-gamesReturns 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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the voice channel |
Query parameters
Section titled “Query parameters”| Field | Type | Description |
|---|---|---|
| connection_id | string | The caller’s voice connection in this channel (1-64 characters) |
Request headers
Section titled “Request headers”| Field | Type | Description |
|---|---|---|
| If-None-Match? | string | An ETag from an earlier response to this route |
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| tables | array[table game summary object] | The tables of the room |
Response headers
Section titled “Response headers”| Field | Type | Description |
|---|---|---|
| ETag | string | A strong tag over the response body, sent on 200 and on 304 |
| Cache-Control | string | The literal value private, no-cache |
| Vary | string | The literal value Authorization, replaced by Origin where the cross-origin policy echoed an allowed origin |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The tables were listed |
| 304 | empty | The request sent a matching If-None-Match |
| 400 | error response | connection_id is absent or fails the membership check, returned as USER_NOT_IN_VOICE |
The common responses also apply.
Rate limit
Section titled “Rate limit”60 requests per 10 seconds for each authenticated user and channel ID, on the shared table_game:read::channel_id bucket.
Get a table game
Section titled “Get a table game”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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the voice channel |
| slot | string | The table slot, T and two digits such as T01 |
Query parameters
Section titled “Query parameters”| Field | Type | Description |
|---|---|---|
| connection_id? | string | The caller’s voice connection in this channel (1-64 characters) |
Request headers
Section titled “Request headers”| Field | Type | Description |
|---|---|---|
| If-None-Match? | string | An ETag from an earlier response to this route |
Response headers
Section titled “Response headers”| Field | Type | Description |
|---|---|---|
| ETag | string | A strong tag of the table version and the caller’s position, sent on 200 and on 304 |
| Cache-Control | string | The literal value private, no-cache |
| Vary | string | The literal value Authorization, replaced by Origin where the cross-origin policy echoed an allowed origin |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | table game view object | The table was returned |
| 304 | empty | The request sent a matching If-None-Match |
| 400 | error response | A caller with no ticket sent no connection_id or one that fails the check, returned as USER_NOT_IN_VOICE |
| 404 | error response | The slot has no table, returned as UNKNOWN_TABLE_GAME |
The common responses also apply.
Rate limit
Section titled “Rate limit”60 requests per 10 seconds for each authenticated user and channel ID, on the shared table_game:read::channel_id bucket.
Open a table game
Section titled “Open a table game”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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the voice channel |
| slot | string | A table slot of the channel’s room theme |
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| game | string | The game kind |
| speed | string | The game speed |
| connection_id | string | The caller’s voice connection in this channel (1-64 characters) |
| ranked? | boolean | Whether the table is ranked, false by default |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 201 | table game view object | The lobby was opened |
| 400 | error response | connection_id fails the membership check, returned as USER_NOT_IN_VOICE |
| 400 | error response | ranked is true for tavla, returned as INVALID_FORM_BODY |
| 403 | error response | ranked is true while ranked tables are off, returned as FEATURE_TEMPORARILY_DISABLED |
| 403 | error response | ranked is true and the account fails a seat check, as TABLE_GAME_RANKED_NOT_ELIGIBLE |
| 404 | error response | The slot is not in the channel’s room theme, returned as UNKNOWN_TABLE_GAME |
| 409 | error response | The slot has an open table, or the caller plays at another one, returned as TABLE_GAME_CONFLICT |
The common responses also apply.
Side effects
Section titled “Side effects”The caller’s connection moves to the table’s position 1 seat, and the table’s players receive a Table Game Update Dispatch.
Rate limit
Section titled “Rate limit”5 requests per minute for each authenticated user and channel ID, on the table_game:create::channel_id bucket.
Join a table game
Section titled “Join a table game”PUT/v1/channels/{channel_id}/table-games/{slot}/players/@meSits 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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the voice channel |
| slot | string | A table slot of the channel’s room theme |
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| position | integer | The position to take (1-4), where chess, both damas, and tavla use 1 and 3 only |
| connection_id | string | The caller’s voice connection in this channel (1-64 characters) |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | table game view object | The caller sat down |
| 400 | error response | connection_id fails the membership check, returned as USER_NOT_IN_VOICE |
| 403 | error response | The table is ranked and the account fails a seat check, as TABLE_GAME_RANKED_NOT_ELIGIBLE |
| 404 | error response | The slot is not in the room theme or has no table, returned as UNKNOWN_TABLE_GAME |
| 409 | error response | The table is not a lobby, or the position is taken or unused by the game, as TABLE_GAME_CONFLICT |
| 409 | error response | The caller already plays at this or another table, returned as TABLE_GAME_CONFLICT |
The common responses also apply.
Side effects
Section titled “Side effects”The caller’s connection moves to the position’s seat, and the table’s players and spectators receive a Table Game Update Dispatch.
Rate limit
Section titled “Rate limit”10 requests per 30 seconds for each authenticated user, channel ID, and table slot, on the shared table_game:seat::channel_id::slot bucket.
Leave a table game
Section titled “Leave a table game”DELETE/v1/channels/{channel_id}/table-games/{slot}/players/@meGives 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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the voice channel |
| slot | string | The table slot |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 204 | empty | The caller left the table |
| 404 | error response | The slot has no table, returned as UNKNOWN_TABLE_GAME |
| 409 | error response | The caller does not play at the table, returned as TABLE_GAME_CONFLICT |
The common responses also apply.
Side effects
Section titled “Side effects”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.
Rate limit
Section titled “Rate limit”10 requests per 30 seconds for each authenticated user, channel ID, and table slot, on the shared table_game:seat::channel_id::slot bucket.
Act at a table game
Section titled “Act at a table game”POST/v1/channels/{channel_id}/table-games/{slot}/actionsApplies 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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the voice channel |
| slot | string | The table slot |
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| version | integer | The table version the action was made against, ignored for tick |
| action | table game action object | The 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.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | table game view object | The action was applied |
| 400 | error response | The rules refuse the action, returned as TABLE_GAME_RULE_VIOLATION with a reason |
| 404 | error response | The slot has no table, returned as UNKNOWN_TABLE_GAME |
| 409 | error response | The caller does not play at the table, which includes every spectator, as TABLE_GAME_CONFLICT |
| 409 | error response | The version is stale, or a move is sent to a lobby, returned as TABLE_GAME_CONFLICT |
The common responses also apply.
Side effects
Section titled “Side effects”An action that changes the table sends a Table Game Update Dispatch to its players and subscribed spectators before the response.
Rate limit
Section titled “Rate limit”30 requests per 10 seconds for each authenticated user, channel ID, and table slot, on the table_game:action::channel_id::slot bucket.
Watch a table game
Section titled “Watch a table game”PUT/v1/channels/{channel_id}/table-games/{slot}/spectators/@meSubscribes 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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the voice channel |
| slot | string | The table slot |
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| connection_id | string | The caller’s voice connection in this channel (1-64 characters) |
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| subscribed | boolean | Whether the caller receives the table’s Dispatches, false while the spectators are full |
| view | table game view object | The public view of the table |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The caller watches the table |
| 400 | error response | connection_id fails the membership check, returned as USER_NOT_IN_VOICE |
| 404 | error response | The slot has no table, returned as UNKNOWN_TABLE_GAME |
| 409 | error response | The caller plays at the table, returned as TABLE_GAME_CONFLICT |
The common responses also apply.
Side effects
Section titled “Side effects”The caller stops watching any other table, subscribed or not.
Rate limit
Section titled “Rate limit”10 requests per minute for each authenticated user and channel ID, on the shared table_game:spectate::channel_id bucket.
Stop watching a table game
Section titled “Stop watching a table game”DELETE/v1/channels/{channel_id}/table-games/{slot}/spectators/@meEnds the caller’s subscription to the table’s updates and returns 204 with an empty body, whether or not the caller was subscribed.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| channel_id | snowflake | The ID of the voice channel |
| slot | string | The table slot |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 204 | empty | The caller no longer watches the table |
The common responses also apply.
Rate limit
Section titled “Rate limit”10 requests per minute for each authenticated user and channel ID, on the shared table_game:spectate::channel_id bucket.
Get table game rewards
Section titled “Get table game rewards”GET/v1/table-games/rewardsReturns 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.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | table game reward showcase object | The showcase was returned |
| 401 | error response | No account is signed in |
| 403 | error response | The credential is a bot or OAuth2 bearer token, returned as ACCESS_DENIED |
| 403 | error response | Ranked table games are switched off, returned as FEATURE_TEMPORARILY_DISABLED |
Rate limit
Section titled “Rate limit”20 requests per 10 seconds for each authenticated user, on the table_game:rewards bucket.
Get a table game leaderboard
Section titled “Get a table game leaderboard”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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| game | string | A game kind other than tavla |
Query parameters
Section titled “Query parameters”| Field | Type | Description |
|---|---|---|
| season? | string | current or previous, current by default |
| offset? | integer | The places to skip (0-950), 0 by default |
| limit? | integer | The most entries to return (1-50), 50 by default |
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| game | string | The game kind |
| season | integer | The season as YYYYMM |
| total | integer | The players on the leaderboard |
| entries | array[table game leaderboard entry object] | The page, in place order |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The page was returned |
| 400 | error response | A parameter is invalid, returned as INVALID_FORM_BODY |
| 401 | error response | No account is signed in |
| 403 | error response | The credential is a bot or OAuth2 bearer token, returned as ACCESS_DENIED |
| 403 | error response | Ranked tables are off, returned as FEATURE_TEMPORARILY_DISABLED |
Rate limit
Section titled “Rate limit”20 requests per 10 seconds for each authenticated user, on the table_game:leaderboard bucket.
Get a guild table game leaderboard
Section titled “Get a guild table game leaderboard”GET/v1/guilds/{guild_id}/table-games/leaderboardReturns 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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| guild_id | snowflake | The ID of the guild |
Query parameters
Section titled “Query parameters”| Field | Type | Description |
|---|---|---|
| week? | string | current or previous, current by default |
| offset? | integer | The places to skip (0-950), 0 by default |
| limit? | integer | The most entries to return (1-50), 50 by default |
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| week | integer | The Monday that starts the week, as YYYYMMDD |
| starts_at | ISO8601 timestamp | The start of the week |
| ends_at | ISO8601 timestamp | The start of the next week |
| total | integer | The members on the leaderboard |
| entries | array[guild table game leaderboard entry object] | The page, in place order |
| me | ?standing object | The caller’s standing, null when the caller has no rated game there that week |
Standing object
Section titled “Standing object”| Field | Type | Description |
|---|---|---|
| points | integer | The caller’s points in the guild that week |
| games | integer | The caller’s rated games in the guild that week |
| rank | ?integer | The caller’s place, null when the caller is not on the leaderboard |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The page was returned |
| 400 | error response | A parameter is invalid, returned as INVALID_FORM_BODY |
| 401 | error response | No account is signed in |
| 403 | error response | The credential is a bot or OAuth2 bearer token, returned as ACCESS_DENIED |
| 403 | error response | The caller is not a member, or the guild does not exist, as MISSING_ACCESS |
| 403 | error response | Ranked tables are off, returned as FEATURE_TEMPORARILY_DISABLED |
Rate limit
Section titled “Rate limit”20 requests per 10 seconds for each authenticated user and guild ID, on the guild:table_game_leaderboard::guild_id bucket.
Get my table game ratings
Section titled “Get my table game ratings”GET/v1/users/@me/table-game-ratingsReturns 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.
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| listed | boolean | Whether the caller shows on leaderboards |
| ratings | array[table game rating summary object] | One summary per game kind, in game kind order |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The standing was returned |
| 401 | error response | No account is signed in |
| 403 | error response | The credential is a bot or OAuth2 bearer token, returned as ACCESS_DENIED |
| 403 | error response | Ranked tables are off, returned as FEATURE_TEMPORARILY_DISABLED |
Rate limit
Section titled “Rate limit”30 requests per 10 seconds for each authenticated user, on the table_game:ratings:me bucket.