플레이어
플레이어 세션을 강제로 끝내거나 차단 · 차단 해제하고, 지금 게임에 접속 중인 플레이어를 조회합니다.
플레이어는 POST /launch 를 처음 부를 때 플랫폼에 등록됩니다. 경로의 {external_id} 는 launch 때 보낸 player.external_id 입니다(URL 경로에 넣을 때는 퍼센트 인코딩하고, 인코딩한 그대로 서명하세요 — 인증 · 서명).
POST /players/{external_id}/logout
POST {API_BASE}/operator/v1/players/{external_id}/logout플레이어의 모든 게임 세션을 즉시 끝냅니다. block 으로 차단 · 차단 해제도 함께 정합니다. 오퍼레이터 사이트에서 회원이 로그아웃 · 탈퇴 · 정지 · 자기 배제될 때 부르세요.
호출하면 다음이 순서대로 일어납니다.
- 플레이어 상태를
block값대로 정합니다(true= 차단,false= 정상). - 플레이어의 모든 세션을 폐기하고 열린 게임 화면 연결을 끊습니다. 게임 화면은 세션 만료 화면으로 바뀌고
session.expired이벤트를 보냅니다(게임 실행 흐름). - 모든 테이블에서 자리를 비웁니다. 진행 중인 판은 끝까지 진행된 뒤 정산되고, 바이인형 게임은 남은 칩을 캐시아웃합니다. 돈은 평소처럼 지갑 콜백(
credit)으로 돌아갑니다.
경로 · 요청 필드
본문은 생략할 수 있습니다(빈 본문으로 서명). 생략하면 reason: "operator_logout", block: false 입니다.
| 필드 | 위치 | 타입 | 필수 | 설명 |
|---|---|---|---|---|
external_id | 경로 | string | 필수 | 오퍼레이터 쪽 플레이어 ID |
reason | 본문 | string | 종료 사유. 변경 이력에 남고 게임 화면 연결 종료 사유로 전달됩니다. 기본 operator_logout | |
block | 본문 | boolean | true 면 차단, false(기본)면 정상 상태로 둡니다 |
reason 은 자유 문자열입니다. 다음처럼 용도별로 나눠 쓰면 변경 이력에서 구분하기 쉽습니다.
| 용도 | 예 |
|---|---|
| 사이트 로그아웃 · 관리자 강제 종료 | {"reason": "operator_logout"} |
| 회원 정지 · 탈퇴 | {"reason": "operator_block", "block": true} |
| 정지 해제 | {"reason": "operator_unblock", "block": false} |
| 자기 배제(책임 있는 게임) | {"reason": "self_exclusion", "block": true} 또는 사이트에서 따로 막는다면 block: false |
block 은 매번 덮어씁니다
이 호출은 플레이어 상태를 항상 block 값으로 덮어씁니다. 차단된 플레이어에게 block 을 빼고(또는 false 로) 호출하면 차단이 풀립니다. 차단된 플레이어의 세션만 다시 끊으려면 block: true 를 함께 보내세요.
- 차단된 플레이어는
POST /launch가403 PLAYER_BLOCKED이고, 이미 발급된 실행 URL 로도 들어갈 수 없습니다. - 차단 해제(
block: false)도 그 순간의 세션은 끊습니다. 해제 뒤에는 새로 launch 하면 들어갈 수 있습니다. - 한 번도 launch 하지 않은
external_id는 플랫폼에 없으므로404 PLAYER_NOT_FOUND입니다. 사이트 회원 정지 처리 흐름에서는 이 오류를 정상으로 취급해도 됩니다.
요청 예시
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자리 16진수)
{ "reason": "operator_block", "block": true }응답
200 OK
{ "status": "OK", "sessions_closed": 2 }| 필드 | 타입 | 설명 |
|---|---|---|
status | string | 항상 OK |
sessions_closed | integer | 끊은 세션 수. 접속 중이 아니었으면 0 |
응답 헤더 X-Audit-Id 로 이 조작의 변경 이력 번호를 받습니다.
오류
| HTTP | error.code | 원인 |
|---|---|---|
| 404 | PLAYER_NOT_FOUND | 이 오퍼레이터에 그 external_id 의 플레이어가 없음 |
| 500 | INTERNAL_ERROR | 상태 저장 실패. 다시 시도하세요(같은 요청을 여러 번 보내도 결과는 같습니다). |
GET /players/online
GET {API_BASE}/operator/v1/players/online이 오퍼레이터의 플레이어 중 지금 게임 화면에 접속 중인 사람과, 각자 보고 있거나 앉아 있는 테이블입니다. 관리 화면의 실시간 접속자 목록이나 강제 종료 대상 찾기에 씁니다.
- 게임 화면 연결(WebSocket)이 있는 플레이어만 나옵니다. 창을 닫아 연결이 끊긴 플레이어는 테이블에 자리가 남아 있어도 목록에 없습니다.
- 접속을 가장 오래 유지한 플레이어부터 나옵니다. 플레이어마다 앉아 있는 테이블이 먼저 옵니다.
- 페이지 나누기 없이 전체를 한 번에 줍니다.
응답
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
}
]
}
]
}| 필드 | 타입 | 설명 |
|---|---|---|
at | string | 이 목록을 만든 시각 |
players[].player_id | string | 플랫폼 플레이어 ID(pl_…) |
players[].external_id | string | 오퍼레이터 쪽 플레이어 ID |
players[].nickname | string | 게임 화면 이름 |
players[].currency | string | 플레이어 통화 |
players[].ip | string | 플레이어 브라우저의 접속 IP |
players[].user_agent | string | 플레이어 브라우저의 User-Agent |
players[].connected_at | string | 지금 연결 중 가장 먼저 연결된 시각 |
players[].connections | integer | 열려 있는 게임 화면 연결 수(탭 · 창 수) |
players[].in_lobby | boolean | 게임 로비 화면을 보고 있는지 |
players[].tables | array | 보고 있거나 앉아 있는 테이블. 없으면 [] |
tables[].table_id · game_code · name · names | 테이블 정보(GET /tables 와 같은 뜻) | |
tables[].seated | boolean | 이 테이블에 앉아 있는지 |
tables[].seat | integer | 좌석 번호. 앉아 있을 때만 |
tables[].chips | integer | 지금 칩(최소 단위). 앉아 있을 때만 |
tables[].buy_in_total | integer | 이번 착석의 바이인 · 충전 합계(최소 단위). 앉아 있을 때만 |
tables[].seated_at | string | 앉은 시각. 앉아 있을 때만 |
시각 필드는 RFC 3339 형식이며, 소수 초 자리 수와 시간대 오프셋이 고정되어 있지 않습니다. 표준 RFC 3339 파서로 읽으세요(공통 규약).
오류
인증 · 공통 오류만 있습니다(오류 코드).