Table games
A ranked table game season is one Istanbul calendar month. Two hours after a season ends, a background job freezes the first 100 places of each ranked game’s leaderboard and writes one reward candidate per place. A second job reads each candidate’s games of that season and either marks the candidate ineligible or puts it up for review. Nothing is given until staff approve a candidate with the routes on this page. These routes also set the rewards of the five tiers, read the table games of one account, and reset, exclude and reinstate a player. Every route on this page requires table_game:manage and records an Admin audit entry, reads included.
A value the server cannot accept returns 400 INVALID_FORM_BODY, with one element in errors for each refused field and an English message.
Reward tiers
Section titled “Reward tiers”| Value | Description |
|---|---|
| rank1 | First place |
| rank2 | Second place |
| rank3 | Third place |
| top10 | Places 4 through 10 |
| top100 | Places 11 through 100 |
A place that is not taken, or whose candidate is refused, is not passed on: the places stay as they were frozen.
Season reward object
Section titled “Season reward object”The type field decides the other field.
| Field | Type | Description |
|---|---|---|
| type | string | premium_days, cosmetic, badge or offsite |
| days1 | integer | The Micup Plus days granted (1-365) |
| item_id2 | snowflake | The ID of an avatar decoration |
| badge_id3 | snowflake | The ID of a badge |
| description4 | string | What staff hand over by hand (1-200 characters) |
1 Present for premium_days
2 Present for cosmetic. The item must be an avatar decoration with owned availability that is not in the shop catalog and is not a Visionary reward
3 Present for badge. The badge must exist and must not be numbered
4 Present for offsite
Season rewards object
Section titled “Season rewards object”| Field | Type | Description |
|---|---|---|
| rank1 | array[season reward object] | The rewards of first place |
| rank2 | array[season reward object] | The rewards of second place |
| rank3 | array[season reward object] | The rewards of third place |
| top10 | array[season reward object] | The rewards of places 4 through 10 |
| top100 | array[season reward object] | The rewards of places 11 through 100 |
| showcase_title | string | The showcase heading, trimmed and at most 80 characters; an empty string uses the client’s translated default |
| updated_at | ?ISO8601 timestamp | When the rewards were last saved, null before the first save |
| chance_rewards_enabled | boolean | Whether okey and okey 101 candidates receive Micup Plus days |
The rewards live in the instance configuration under the table_game_rewards_config key, and no public instance route returns them. Signed-in users read the reward showcase. The showcase heading and off-site reward descriptions are visible to those users. The feature_table_game_chance_rewards limit key sets chance_rewards_enabled. It is 0 by default, and while it is 0 a premium_days reward of an okey or okey 101 candidate is withheld.
Candidate statuses
Section titled “Candidate statuses”| Value | Description |
|---|---|
| analyzing | The candidate’s games are being read |
| review | The candidate waits for a staff decision |
| ineligible | The account was gone, banned or limited at close, met too few opponents, or is excluded from the season |
| approved | Staff approved the reward, and its delivery is queued |
| rejected | Staff refused the reward |
| delivering | The reward is being given |
| delivered | Every reward was given |
| pending_offsite | Every reward was given except an off-site one, which staff hand over by hand |
| revoked | Staff took the reward back after it was given |
| void | The account was gone, banned, limited or excluded from the season when the reward was to be given |
A candidate moves only along these steps:
| Action | Moves |
|---|---|
| approve | From review or rejected to approved |
| reject | From review or approved to rejected |
| mark_offsite_delivered | From pending_offsite to delivered |
| revoke | From delivered or pending_offsite to revoked |
| reconsider | From ineligible to review, unless the candidate is flagged account_gone or account_banned |
Candidate flags
Section titled “Candidate flags”| Value | Description |
|---|---|
| account_gone | The account was deleted, self-deleted, marked as a spammer or scheduled for deletion at close |
| account_banned | The account was banned at close |
| account_limited | The account was limited at close |
| few_opponents | The account met fewer than 10 different rated opponents in the season |
| closed_cluster | Half or more of at least 10 wins came against 3 opponents or fewer |
| one_way_flow | The account won 80% or more of at least 10 rated games against one opponent |
| flagged_matches | At least 3 of the season’s games carry a match flag |
| flagged_opponents | One of the 20 opponents beaten most is gone, banned, limited, or under a sanction of the last 400 days |
| history_truncated | The season holds more games than the analysis reads, and only the newest were read |
| own_sanction | The account itself has a sanction not lifted: an exclusion from this season and game, or one of the last 400 days |
A flag only orders the review and keeps a candidate out of Approve unflagged candidates. The decision is the reviewer’s.
The analysis reads at most 20000 rated games of the candidate’s game and 100000 ranked games in all. A candidate flagged history_truncated and few_opponents goes to review, not ineligible, since the games not read may hold more opponents. An exclusion from the season and game makes a candidate ineligible whatever its other flags; any other sanction of its own, in any game, only flags it.
Withheld reasons
Section titled “Withheld reasons”| Value | Description |
|---|---|
| chance_game | A Micup Plus reward of an okey or okey 101 candidate while chance rewards are off |
| item_unavailable | The avatar decoration no longer existed when the reward was given |
| badge_missing | The badge no longer existed when the reward was given |
Stored reward object
Section titled “Stored reward object”| Field | Type | Description |
|---|---|---|
| reward | season reward object | The reward as the tiers stood when the season closed |
| withheld | ?string | The withheld reason of a reward that is not given, otherwise null |
Candidate signals object
Section titled “Candidate signals object”| Field | Type | Description |
|---|---|---|
| games | integer | The rated games of the season in this game |
| wins | integer | The games the account won |
| distinct_opponents | integer | The different rated opponents the account met |
| top3_win_share | number | The share of wins taken from the 3 opponents beaten most (0-1) |
| one_way | array[one way opponent object] | The opponents behind a one_way_flow flag |
| flagged_matches | integer | The games that carry a match flag |
| flagged_opponents | array[snowflake] | The opponents behind a flagged_opponents flag |
| truncated | boolean | Whether only the newest games were read |
One way opponent object
Section titled “One way opponent object”| Field | Type | Description |
|---|---|---|
| user_id | snowflake | The ID of the opponent |
| games | integer | The rated games against the opponent |
| win_rate | number | The share of those games the account won |
Table game candidate object
Section titled “Table game candidate object”| Field | Type | Description |
|---|---|---|
| season | integer | The season as YYYYMM |
| game | string | The game kind |
| user_id | snowflake | The ID of the account |
| user | ?partial user object | The account, null when it no longer exists |
| rank | integer | The frozen place (1-100) |
| tier | string | The reward tier |
| score | number | The leaderboard score at close |
| league | string | The league at close |
| rr | integer | The rank rating at close, 0 below the legend league |
| status | string | The candidate status |
| flags | array[string] | The candidate flags |
| signals | ?candidate signals object | The summary of the season’s games, null before analysis |
| rewards | array[stored reward object] | The rewards of the tier, frozen at close |
| decided_by | ?snowflake | The admin who last moved the candidate |
| decided_at | ?ISO8601 timestamp | When the candidate was last moved |
| delivered_at | ?ISO8601 timestamp | When the rewards were given, or the off-site reward was marked handed over |
| note | ?string | The last staff note |
| premium_kept?1 | boolean | Whether a revoke left Micup Plus days in place |
1 Present only in the response to a revoke
Table game season object
Section titled “Table game season object”| Field | Type | Description |
|---|---|---|
| season | integer | The season as YYYYMM |
| game | string | The game kind |
| status | string | none before close, building while candidates are written, built after |
| built_at | ?ISO8601 timestamp | When every candidate was written |
| candidates | array[table game candidate object] | Every candidate, flagged ones first, then by place |
Bulk approve result object
Section titled “Bulk approve result object”| Field | Type | Description |
|---|---|---|
| approved | integer | The candidates this request approved |
Sanction kinds
Section titled “Sanction kinds”| Value | Description |
|---|---|
| reset_rating | The rating of one game went back to its starting values, and this season’s leaderboard entry was removed |
| season_exclusion | The account was taken off one season’s leaderboard of one game |
| reward_revoke | A reward was revoked, written by Decide table game candidate |
Table game sanction object
Section titled “Table game sanction object”| Field | Type | Description |
|---|---|---|
| id | snowflake | The ID of the sanction |
| kind | string | The sanction kind |
| game | string | The game kind |
| season | ?integer | The season as YYYYMM, null for reset_rating |
| reason | string | Why staff applied the sanction |
| created_by | snowflake | The admin who applied the sanction |
| created_at | ISO8601 timestamp | When the sanction was applied |
| lifted_at | ?ISO8601 timestamp | When the sanction was lifted |
| lifted_by | ?snowflake | The admin who lifted the sanction |
| appeal_note | ?string | The last appeal note |
Table game rating object
Section titled “Table game rating object”| Field | Type | Description |
|---|---|---|
| game | string | The game kind |
| rating | number | The skill rating |
| rd | number | The rating deviation |
| games | integer | The rated games in this game |
| season | integer | The season of season_games as YYYYMM |
| season_games | integer | The rated games in that season |
| league | ?string | The league, null during placement games |
| rr | integer | The rank rating, 0 below the legend league |
| listed | boolean | Whether the account shows on leaderboards |
| excluded_season | ?integer | The season the account is excluded from, as YYYYMM |
Table game analysis object
Section titled “Table game analysis object”| Field | Type | Description |
|---|---|---|
| game | string | The game kind |
| signals | candidate signals object | The summary of the current season’s games so far |
| flags | array[string] | The candidate flags those games would raise |
Table game history entry object
Section titled “Table game history entry object”| Field | Type | Description |
|---|---|---|
| ended_at | ISO8601 timestamp | When the game ended |
| game | string | The game kind |
| guild_id | snowflake | The guild the table was in |
| place | integer | The account’s place in the game |
| players | integer | The seated players |
| outcome | string | win, loss, draw or none |
| rated_opponents | array[snowflake] | The opponents the game was rated against |
| rating_before | integer | The rating before the game |
| rating_after | integer | The rating after the game |
| flags | array[string] | The match flags of the game |
User table games object
Section titled “User table games object”| Field | Type | Description |
|---|---|---|
| ratings | array[table game rating object] | One per ranked game the account has played |
| analysis | array[table game analysis object] | One per rating |
| candidates | array[table game candidate object] | The account’s candidates of the last 14 seasons, newest first |
| sanctions | array[table game sanction object] | Every sanction of the last 400 days, newest first |
| recent_history | array[table game history entry object] | The last 50 rated games of the last 90 days, newest first |
Get table game rewards
Section titled “Get table game rewards”GET/v1/admin/table-games/rewardsReturns the season rewards object. It is read from storage, not from the instance configuration cache.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | season rewards object | The rewards were returned |
Side effects
Section titled “Side effects”The operation records one Admin audit entry with action view_table_game_rewards, target type table_game, target ID 0, and metadata key result_count, the number of rewards in all tiers.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the admin:table_game bucket.
Update table game rewards
Section titled “Update table game rewards”PUT/v1/admin/table-games/rewardsReplaces the rewards of all five tiers and the showcase heading, and returns the season rewards object. A season that has already closed keeps the rewards it was closed with.
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| rank1 | array[season reward object] | The rewards of first place |
| rank2 | array[season reward object] | The rewards of second place |
| rank3 | array[season reward object] | The rewards of third place |
| top10 | array[season reward object] | The rewards of places 4 through 10 |
| top100 | array[season reward object] | The rewards of places 11 through 100 |
| showcase_title | string | The showcase heading, trimmed and at most 80 characters; an empty string uses the client’s translated default |
- A tier holds at most 5 rewards and at most one
premium_daysreward. - A
cosmeticreward must name an avatar decoration withownedavailability that is not in the shop catalog and is not a Visionary reward. Take an item off sale with Take cosmetic item off sale first. - A
badgereward must name an existing badge that is not numbered. - Any other reward type, an avatar ultra, a profile effect or a nameplate included, is refused.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | season rewards object | The rewards were saved |
| 400 | error response | A reward is refused. errors names each refused field, as <tier>.<index>.<field> |
Side effects
Section titled “Side effects”The rewards are saved with a compare-and-set, and every API process picks them up through the instance configuration refresh. A refused request saves nothing. The operation records one entry with action update_table_game_rewards, target type table_game, target ID 0, and a <tier>_old and <tier>_new metadata key for every tier it changed. Each lists the tier’s rewards as type:value pairs, and an off-site reward as offsite alone. A changed heading adds showcase_title_old and showcase_title_new metadata keys.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the admin:table_game bucket.
Get table game season
Section titled “Get table game season”GET/v1/admin/table-games/seasons/{season}/{game}Returns the table game season object of one season and game. A season that has not closed returns status none and no candidates.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| season | integer | The season as YYYYMM (202601-209912) |
| game | string | The ranked game kind: chess, dama, dama_normal, okey or okey101 |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | table game season object | The season was returned |
| 400 | error response | The season or the game is refused |
Side effects
Section titled “Side effects”The operation records one entry with action view_table_game_season, target type table_game_season, the season as target ID, and metadata keys game and result_count, the number of candidates.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the admin:table_game bucket.
Approve unflagged candidates
Section titled “Approve unflagged candidates”POST/v1/admin/table-games/seasons/{season}/{game}/approve-unflaggedApproves every candidate of one season and game that is in review with no flag, and queues each reward. Returns the bulk approve result object. Flagged candidates stay in review for one-by-one decisions.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| season | integer | The season as YYYYMM (202601-209912) |
| game | string | The ranked game kind |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | bulk approve result object | The candidates were approved |
| 400 | error response | The season or the game is refused |
Side effects
Section titled “Side effects”Each approval is a compare-and-set on review, so a candidate another admin moved first is skipped, and a repeated request approves none twice. Each approved candidate queues one delivery job. The operation records one entry with action bulk_approve_table_game_rewards, target type table_game_season, the season as target ID, and metadata keys game and approved.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the admin:table_game bucket.
Decide table game candidate
Section titled “Decide table game candidate”POST/v1/admin/table-games/seasons/{season}/{game}/candidates/{user_id}Moves one candidate along its statuses, and returns the table game candidate object.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| season | integer | The season as YYYYMM (202601-209912) |
| game | string | The ranked game kind |
| user_id | snowflake | The ID of the candidate |
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| action | string | approve, reject, mark_offsite_delivered, revoke or reconsider |
| note? | string | A staff note (1-500 characters), required for reject and revoke |
approvequeues the delivery of the tier’s rewards. Micup Plus days, the avatar decoration and the badge are given once, however many times the job runs.rejectrefuses the reward. An appeal reverses it withapprove.mark_offsite_deliveredrecords that staff handed over the off-site reward.revokewrites areward_revokesanction, and takes back the badge and the avatar decoration unless another delivered reward of the same season gave the account the same one. Micup Plus days stay.reconsidertakes an automatic refusal back to review, so a person looks at the analysis again after an objection. It is refused for an account that was gone or banned at close.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | table game candidate object | The candidate was moved |
| 400 | error response | A value is refused, or a reject or revoke has no note |
| 404 | error response | The account is not a candidate of that season and game, returned as NOT_FOUND |
| 409 | error response | The candidate’s status does not allow the action, or it changed during the request, returned as CONFLICT |
Side effects
Section titled “Side effects”The status moves with a compare-and-set, so two admins never both move one candidate. Only the revoke that moves the status writes its sanction and takes the items back. A revoke that fails while taking them back returns the candidate to its earlier status, so it can be sent again; the repeat writes no second sanction and takes back nothing twice. The operation records one entry with action approve_table_game_reward, reject_table_game_reward, mark_offsite_delivered_table_game_reward, revoke_table_game_reward or reconsider_table_game_reward, target type user, the ID of the account as target ID, and metadata keys season, game, status and, when given, note and premium_kept.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the admin:table_game bucket.
Get user table games
Section titled “Get user table games”GET/v1/admin/users/{user_id}/table-gamesReturns the user table games object of an account: its ratings, an analysis of its games this season, its candidates, its sanctions, and its recent rated games.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| user_id | snowflake | The ID of the account |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | user table games object | The account’s table games were returned |
An account that never played a ranked game returns empty arrays.
Side effects
Section titled “Side effects”The operation records one entry with action view_user_table_games, target type user, the ID of the account as target ID, and metadata key result_count, the number of history entries returned.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the admin:table_game bucket.
Create table game sanction
Section titled “Create table game sanction”POST/v1/admin/users/{user_id}/table-games/sanctionsApplies a sanction to an account in one game, and returns the table game sanction object.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| user_id | snowflake | The ID of the account |
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| kind | string | reset_rating or season_exclusion |
| game | string | The ranked game kind |
| season?1 | integer | The season as YYYYMM (202601-209912) |
| reason | string | Why staff apply the sanction (1-500 characters) |
1 Required for season_exclusion, and ignored for reset_rating
reset_ratingreturns the rating of the game to its starting values, with placement games to play again, and removes the account from the current season’s leaderboard. Whether the account shows on leaderboards stays as it was. The rating before the reset is kept with the sanction.season_exclusionremoves the account from that season’s leaderboard. For the current season, the account’s later games of that season stay off the leaderboard too. For a closed season, a candidate that waits for review or delivery is rejected, and one still being analyzed is madeineligibleby the analysis. A reward being given or already given is not taken back: revoke it with Decide table game candidate.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | table game sanction object | The sanction was applied |
| 400 | error response | A value is refused, or the season has not started |
| 404 | error response | The account has no rating in the game, returned as NOT_FOUND |
| 409 | error response | The rating kept changing during the request, returned as CONFLICT |
Side effects
Section titled “Side effects”The sanction is stored before its effect, so a request that fails can be sent again to finish it. The operation records one entry with action create_table_game_sanction, target type user, the ID of the account as target ID, and metadata keys sanction_id, kind, game, reason and, for an exclusion, season, plus revoke_needed when the excluded season’s reward was being given or already given.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the admin:table_game bucket.
Update table game sanction
Section titled “Update table game sanction”POST/v1/admin/users/{user_id}/table-games/sanctions/{sanction_id}Lifts a season exclusion after an accepted appeal, or adds an appeal note to any sanction. Returns the table game sanction object.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| user_id | snowflake | The ID of the account |
| sanction_id | snowflake | The ID of the sanction |
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| action | string | lift or note |
| note | string | The appeal note (1-500 characters) |
Lifting an exclusion of the current season puts the account back on the leaderboard with its next game. A closed season stays as it was frozen. To give the reward after all, approve the rejected candidate with Decide table game candidate. A rating reset and a reward revoke are never lifted.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | table game sanction object | The sanction was updated |
| 400 | error response | A value is refused |
| 404 | error response | The account has no such sanction, returned as NOT_FOUND |
| 409 | error response | The sanction is not a season exclusion, or it is already lifted, returned as CONFLICT |
Side effects
Section titled “Side effects”A lift is a compare-and-set, so a sanction is lifted once. The operation records one entry with action lift_table_game_sanction or note_table_game_sanction, target type user, the ID of the account as target ID, and metadata keys sanction_id, kind and note.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the admin:table_game bucket.