Players
Force-end player sessions, block and unblock players, and list the players currently connected to games.
A player is registered on the platform the first time you call POST /launch for them. The {external_id} in the path is the player.external_id you sent at launch (percent-encode it when placing it in the URL path, and sign it exactly as encoded — Authentication & signing).
POST /players/{external_id}/logout
POST {API_BASE}/operator/v1/players/{external_id}/logoutImmediately ends all of the player's game sessions. Use block to set blocking / unblocking at the same time. Call this when a member logs out of, leaves, is suspended from, or self-excludes from the operator site.
When called, the following happens in order:
- The player's status is set according to
block(true= blocked,false= active). - All of the player's sessions are revoked and open game screen connections are closed. The game screen switches to the session-expired screen and sends a
session.expiredevent (Game launch flow). - The player leaves their seat at every table. A round in progress is played to completion and then settled, and in buy-in flow games the remaining chips are cashed out. Money is returned through the wallet callbacks (
credit) as usual.
Path & request fields
The body can be omitted (sign an empty body). If omitted, reason: "operator_logout" and block: false apply.
| Field | Location | Type | Required | Description |
|---|---|---|---|---|
external_id | Path | string | Required | Operator-side player ID |
reason | Body | string | Termination reason. Recorded in the audit log and passed as the reason for closing the game screen connection. Default operator_logout | |
block | Body | boolean | true blocks the player; false (default) leaves the player active |
reason is a free-form string. Using distinct values per purpose, as below, makes entries easy to tell apart in the audit log.
| Purpose | Example |
|---|---|
| Site logout / force logout by an admin | {"reason": "operator_logout"} |
| Member suspension / account closure | {"reason": "operator_block", "block": true} |
| Lifting a suspension | {"reason": "operator_unblock", "block": false} |
| Self-exclusion (responsible gaming) | {"reason": "self_exclusion", "block": true}, or block: false if your site blocks the player separately |
block is overwritten on every call
This call always overwrites the player's status with the block value. If you call it for a blocked player without block (or with false), the block is lifted. To only disconnect a blocked player's sessions again, send block: true as well.
- For a blocked player,
POST /launchreturns403 PLAYER_BLOCKED, and launch URLs already issued cannot be used to enter either. - Unblocking (
block: false) also disconnects the sessions active at that moment. After unblocking, the player can enter with a new launch. - An
external_idthat has never been launched does not exist on the platform, so the response is404 PLAYER_NOT_FOUND. In your site's member-suspension flow, you may treat this error as success.
Request example
POST /operator/v1/players/user-10293/logout HTTP/1.1
Host: {API_HOST}
Content-Type: application/json; charset=utf-8
X-Operator: acme
X-Api-Key: ak_5d2c9e0f7a1b3c4d5e6f708192a3b4c5d6e7f809
X-Timestamp: 1792368067512
X-Nonce: 3a7c9e1b5d0f2a4c6e8b0d1f
X-Signature: 6c2e…(64 hex digits)
{ "reason": "operator_block", "block": true }Response
200 OK
{ "status": "OK", "sessions_closed": 2 }| Field | Type | Description |
|---|---|---|
status | string | Always OK |
sessions_closed | integer | Number of sessions disconnected. 0 if the player was not connected |
The X-Audit-Id response header gives the audit log number for this operation.
Errors
| HTTP | error.code | Cause |
|---|---|---|
| 404 | PLAYER_NOT_FOUND | This operator has no player with that external_id |
| 500 | INTERNAL_ERROR | Failed to save the status. Retry (sending the same request multiple times yields the same result). |
GET /players/online
GET {API_BASE}/operator/v1/players/onlineReturns this operator's players who are currently connected to a game screen, with the tables each is watching or seated at. Use it for a real-time connected-players list in your admin screens or to find targets for force logout.
- Only players with a game screen connection (WebSocket) are included. Players whose connection dropped because they closed the window are not listed, even if they still hold a seat at a table.
- Players who have stayed connected the longest come first. For each player, tables where they are seated come first.
- The whole list is returned at once, without pagination.
Response
200 OK
{
"at": "2026-10-19T08:01:07.512345+08:00",
"players": [
{
"player_id": "pl_019a0f3c7b2e7d41a8c35e9f0b1d2c3a",
"external_id": "user-10293",
"nickname": "LuckyAce",
"currency": "PHP",
"ip": "203.0.113.25",
"user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) …",
"connected_at": "2026-10-19T07:42:10.204+08:00",
"connections": 1,
"in_lobby": false,
"tables": [
{
"table_id": "tbl_019a0f3c8e1f7a2b9c4d5e6f7a8b9c0d",
"game_code": "live_holdem",
"name": "Live Hold'em 25/50",
"names": { "ko": "라이브 홀덤 25/50", "en": "Live Hold'em 25/50" },
"seated": true,
"seat": 2,
"chips": 312500,
"buy_in_total": 400000,
"seated_at": "2026-10-19T07:43:02.911+08:00"
},
{
"table_id": "tbl_019a0f3d1a2b7c3d8e4f5a6b7c8d9e0f",
"game_code": "live_baccarat",
"name": "Live Baccarat A",
"names": { "ko": "라이브 바카라 A", "en": "Live Baccarat A" },
"seated": false
}
]
}
]
}| Field | Type | Description |
|---|---|---|
at | string | Time this list was generated |
players[].player_id | string | Platform player ID (pl_…) |
players[].external_id | string | Operator-side player ID |
players[].nickname | string | Name shown on the game screen |
players[].currency | string | Player currency |
players[].ip | string | Connection IP of the player's browser |
players[].user_agent | string | User-Agent of the player's browser |
players[].connected_at | string | Time of the earliest of the current connections |
players[].connections | integer | Number of open game screen connections (tabs / windows) |
players[].in_lobby | boolean | Whether the player is viewing the game lobby screen |
players[].tables | array | Tables the player is watching or seated at. [] if none |
tables[].table_id / game_code / name / names | Table information (same meaning as in GET /tables) | |
tables[].seated | boolean | Whether the player is seated at this table |
tables[].seat | integer | Seat number. Only when seated |
tables[].chips | integer | Current chips (minor units). Only when seated |
tables[].buy_in_total | integer | Total buy-in and top-up for this seating (minor units). Only when seated |
tables[].seated_at | string | Time the player sat down. Only when seated |
Time fields are in RFC 3339 format; the number of fractional-second digits and the time zone offset are not fixed. Parse them with a standard RFC 3339 parser (Common conventions).
Errors
Only authentication and common errors (Error codes).