1866 lines
62 KiB
YAML
1866 lines
62 KiB
YAML
openapi: 3.0.3
|
|
info:
|
|
title: Galaxy Game Lobby Service Public REST API
|
|
version: v1
|
|
description: |
|
|
This specification documents the public authenticated REST contract of
|
|
`galaxy/lobby` served on `LOBBY_PUBLIC_HTTP_ADDR` (default `:8094`).
|
|
|
|
This port is reached exclusively through `Edge Gateway`. Gateway verifies
|
|
the authenticated session and injects the `X-User-ID` header before
|
|
forwarding every request. `Lobby` derives the acting user identity from
|
|
`X-User-ID` only and must never accept identity claims from request bodies.
|
|
|
|
Scope:
|
|
- game lifecycle management (create, update, get, list)
|
|
- enrollment management (open, close, ready-to-start)
|
|
- start lifecycle (start, pause, resume, cancel, retry-start)
|
|
- application flow for public games
|
|
- invite flow for private games
|
|
- membership operations
|
|
- user-facing lists (my games, my applications, my invitations)
|
|
|
|
This specification intentionally does not describe:
|
|
- the internal trusted REST contract (see `api/internal-openapi.yaml`)
|
|
- Redis Stream event contracts (see `README.md`)
|
|
- notification intent contracts (see `../notification/README.md`)
|
|
|
|
Transport rules:
|
|
- request bodies are strict JSON only; unknown fields are rejected
|
|
- all authenticated routes require `X-User-ID` injected by `Edge Gateway`
|
|
- error responses use `{ "error": { "code", "message" } }`
|
|
- stable error codes are `invalid_request`, `conflict`, `subject_not_found`,
|
|
`eligibility_denied`, `name_taken`,
|
|
`race_name_pending_window_expired`,
|
|
`race_name_registration_quota_exceeded`, `forbidden`,
|
|
`internal_error`, and `service_unavailable`
|
|
- `eligibility_denied`, `name_taken`,
|
|
`race_name_pending_window_expired`, and
|
|
`race_name_registration_quota_exceeded` are returned as `422`
|
|
servers:
|
|
- url: http://localhost:8094
|
|
description: Default local public listener for Game Lobby Service.
|
|
tags:
|
|
- name: Games
|
|
description: Game record CRUD and lifecycle queries.
|
|
- name: Enrollment
|
|
description: Enrollment management commands.
|
|
- name: Lifecycle
|
|
description: Start, pause, resume, cancel, and retry-start commands.
|
|
- name: Applications
|
|
description: Application flow for public games.
|
|
- name: Invites
|
|
description: Invite flow for private games.
|
|
- name: Memberships
|
|
description: Membership roster operations.
|
|
- name: MyLists
|
|
description: Authenticated-user personal list queries.
|
|
- name: RaceNames
|
|
description: Race Name Directory user-facing operations.
|
|
- name: Probes
|
|
description: Health and readiness probes.
|
|
paths:
|
|
/healthz:
|
|
get:
|
|
tags:
|
|
- Probes
|
|
operationId: publicHealthz
|
|
summary: Public listener health probe
|
|
responses:
|
|
"200":
|
|
description: Service is alive.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ProbeResponse"
|
|
examples:
|
|
ok:
|
|
value:
|
|
status: ok
|
|
/readyz:
|
|
get:
|
|
tags:
|
|
- Probes
|
|
operationId: publicReadyz
|
|
summary: Public listener readiness probe
|
|
responses:
|
|
"200":
|
|
description: Service is ready to serve traffic.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ProbeResponse"
|
|
examples:
|
|
ready:
|
|
value:
|
|
status: ready
|
|
/api/v1/lobby/games:
|
|
post:
|
|
tags:
|
|
- Games
|
|
operationId: createGame
|
|
summary: Create a new game record in draft status
|
|
description: |
|
|
Creates a new game record in `draft` status.
|
|
|
|
Authorization:
|
|
- `game_type=public`: requires system-admin role enforced upstream by
|
|
`Admin Service`; public games created on the internal port only in
|
|
normal operation
|
|
- `game_type=private`: requires the acting user's eligibility snapshot
|
|
from `User Service` to carry `can_create_private_game=true`
|
|
parameters:
|
|
- $ref: "#/components/parameters/XUserID"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/CreateGameRequest"
|
|
responses:
|
|
"201":
|
|
description: Game record created in draft status.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"422":
|
|
$ref: "#/components/responses/DomainPreconditionError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
get:
|
|
tags:
|
|
- Games
|
|
operationId: listGames
|
|
summary: List public games with deterministic pagination
|
|
description: |
|
|
Returns a paginated list of public games with status in
|
|
`enrollment_open`, `ready_to_start`, `running`, or `finished`.
|
|
|
|
Games in `draft` or `cancelled` status are excluded from the public
|
|
list. Authenticated users also see private games where they hold an
|
|
active membership.
|
|
|
|
Default order: `enrollment_open` and `ready_to_start` first, then
|
|
`running`, then `finished` (most recent first within each group).
|
|
parameters:
|
|
- $ref: "#/components/parameters/XUserID"
|
|
- $ref: "#/components/parameters/PageSize"
|
|
- $ref: "#/components/parameters/PageToken"
|
|
responses:
|
|
"200":
|
|
description: One deterministic page of game summaries.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameListResponse"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}:
|
|
get:
|
|
tags:
|
|
- Games
|
|
operationId: getGame
|
|
summary: Get one game record
|
|
description: |
|
|
Returns the full game record for the requested `game_id`.
|
|
|
|
Visibility rules:
|
|
- private `draft` games: visible only to the owner
|
|
- private non-draft games: visible to the owner and users with an
|
|
active membership or a non-expired invite
|
|
- public `draft` games: visible only to system administrators
|
|
- public non-draft games: visible to any authenticated user
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Full game record.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
patch:
|
|
tags:
|
|
- Games
|
|
operationId: updateGame
|
|
summary: Update mutable fields of a game record
|
|
description: |
|
|
Partially updates a game record.
|
|
|
|
Only fields present in the request body are modified; absent fields
|
|
retain their current values.
|
|
|
|
Editable in `draft` status: all fields in the request schema.
|
|
Editable in `enrollment_open` status: `description` only.
|
|
All fields are immutable in all other statuses.
|
|
|
|
Authorization: system administrator or private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/UpdateGameRequest"
|
|
responses:
|
|
"200":
|
|
description: Updated game record.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/open-enrollment:
|
|
post:
|
|
tags:
|
|
- Enrollment
|
|
operationId: openEnrollment
|
|
summary: Transition a draft game to enrollment_open
|
|
description: |
|
|
Transitions the game from `draft` to `enrollment_open`.
|
|
|
|
Authorization: system administrator or private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Updated game record with status enrollment_open.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/ready-to-start:
|
|
post:
|
|
tags:
|
|
- Enrollment
|
|
operationId: manualReadyToStart
|
|
summary: Manually close enrollment and transition to ready_to_start
|
|
description: |
|
|
Manually closes enrollment and transitions the game from
|
|
`enrollment_open` to `ready_to_start`.
|
|
|
|
Pre-condition: `approved_count >= min_players`.
|
|
|
|
Side effects: all invites in `created` status are transitioned to
|
|
`expired`; `lobby.invite.expired` notification intents are published
|
|
for each expired invite.
|
|
|
|
Authorization: system administrator or private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Updated game record with status ready_to_start.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/start:
|
|
post:
|
|
tags:
|
|
- Lifecycle
|
|
operationId: startGame
|
|
summary: Initiate the game start sequence
|
|
description: |
|
|
Transitions the game from `ready_to_start` to `starting` and publishes
|
|
a start job to `Runtime Manager`.
|
|
|
|
The final outcome (`running`, `paused`, or `start_failed`) is determined
|
|
asynchronously by the `Runtime Manager` result consumer.
|
|
|
|
Authorization: system administrator or private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Updated game record with status starting.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/pause:
|
|
post:
|
|
tags:
|
|
- Lifecycle
|
|
operationId: pauseGame
|
|
summary: Apply a platform-level pause to a running game
|
|
description: |
|
|
Transitions the game from `running` to `paused`.
|
|
|
|
This is a platform-level pause distinct from `Game Master` runtime
|
|
failure states. The engine container may remain alive.
|
|
|
|
Authorization: system administrator or private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Updated game record with status paused.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/resume:
|
|
post:
|
|
tags:
|
|
- Lifecycle
|
|
operationId: resumeGame
|
|
summary: Resume a paused game
|
|
description: |
|
|
Transitions the game from `paused` to `running`.
|
|
|
|
A synchronous `Game Master` liveness check is performed before the
|
|
transition. If `Game Master` is unreachable, the game remains `paused`
|
|
and `503 service_unavailable` is returned.
|
|
|
|
Authorization: system administrator or private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Updated game record with status running.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/cancel:
|
|
post:
|
|
tags:
|
|
- Lifecycle
|
|
operationId: cancelGame
|
|
summary: Cancel a game that has not yet started running
|
|
description: |
|
|
Cancels the game. Allowed source statuses: `draft`, `enrollment_open`,
|
|
`ready_to_start`, `start_failed`. Not allowed from `starting`,
|
|
`running`, or `paused`.
|
|
|
|
Authorization: system administrator or private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Updated game record with status cancelled.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/retry-start:
|
|
post:
|
|
tags:
|
|
- Lifecycle
|
|
operationId: retryStart
|
|
summary: Retry a failed start attempt
|
|
description: |
|
|
Transitions the game from `start_failed` back to `ready_to_start`,
|
|
enabling a new start attempt.
|
|
|
|
Authorization: system administrator or private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Updated game record with status ready_to_start.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/applications:
|
|
post:
|
|
tags:
|
|
- Applications
|
|
operationId: submitApplication
|
|
summary: Submit a join application for a public game
|
|
description: |
|
|
Creates a new application in `submitted` status for a public game.
|
|
|
|
Pre-conditions checked synchronously:
|
|
- game status is `enrollment_open` and game type is `public`
|
|
- acting user has no existing non-rejected application to the same game
|
|
- `User Service` eligibility confirms `can_join_game=true`
|
|
- roster capacity allows additional applicants
|
|
- Race Name Directory confirms `race_name` is available for the acting user
|
|
|
|
On success, `lobby.application.submitted` notification intent is
|
|
published to the configured admin email list.
|
|
|
|
Authorization: any authenticated user.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/SubmitApplicationRequest"
|
|
responses:
|
|
"201":
|
|
description: Application created in submitted status.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ApplicationRecord"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"422":
|
|
$ref: "#/components/responses/DomainPreconditionError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/applications/{application_id}/approve:
|
|
post:
|
|
tags:
|
|
- Applications
|
|
operationId: approveApplication
|
|
summary: Approve a submitted application
|
|
description: |
|
|
Approves a submitted application, reserves the race name, and creates
|
|
an active membership for the applicant.
|
|
|
|
Pre-conditions: game is `enrollment_open`; application is `submitted`;
|
|
roster capacity allows additional approved participants.
|
|
|
|
On success, `lobby.membership.approved` notification intent is published
|
|
to the applicant.
|
|
|
|
Authorization: system administrator.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/ApplicationIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Active membership created for the approved applicant.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/MembershipRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/applications/{application_id}/reject:
|
|
post:
|
|
tags:
|
|
- Applications
|
|
operationId: rejectApplication
|
|
summary: Reject a submitted application
|
|
description: |
|
|
Rejects a submitted application and releases any pending race name
|
|
reservation held for the applicant.
|
|
|
|
On success, `lobby.membership.rejected` notification intent is published
|
|
to the applicant.
|
|
|
|
Authorization: system administrator.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/ApplicationIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Application record with status rejected.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ApplicationRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/invites:
|
|
post:
|
|
tags:
|
|
- Invites
|
|
operationId: createInvite
|
|
summary: Create an invite for a private game
|
|
description: |
|
|
Creates a new invite in `created` status for the specified invitee.
|
|
|
|
Pre-conditions: game is `enrollment_open` and `private`; the invitee
|
|
has no active invite or active membership in the game; roster capacity
|
|
allows additional participants.
|
|
|
|
`expires_at` is set to `enrollment_ends_at` of the game.
|
|
|
|
On success, `lobby.invite.created` notification intent is published
|
|
to the invitee.
|
|
|
|
Authorization: private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/CreateInviteRequest"
|
|
responses:
|
|
"201":
|
|
description: Invite record created in created status.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/InviteRecord"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/invites/{invite_id}/redeem:
|
|
post:
|
|
tags:
|
|
- Invites
|
|
operationId: redeemInvite
|
|
summary: Redeem an invite and join a private game
|
|
description: |
|
|
Redeems a `created` invite, reserves the chosen race name, and creates
|
|
an active membership immediately without a separate owner-approval step.
|
|
|
|
Pre-conditions: invite status is `created`; game is `enrollment_open`;
|
|
roster capacity allows additional participants; Race Name Directory
|
|
confirms `race_name` is available for the acting user.
|
|
|
|
On success, `lobby.invite.redeemed` notification intent is published
|
|
to the private-game owner.
|
|
|
|
Authorization: the invited user (invitee_user_id must match X-User-ID).
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/InviteIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/RedeemInviteRequest"
|
|
responses:
|
|
"200":
|
|
description: Active membership created for the redeeming user.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/MembershipRecord"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"422":
|
|
$ref: "#/components/responses/DomainPreconditionError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/invites/{invite_id}/decline:
|
|
post:
|
|
tags:
|
|
- Invites
|
|
operationId: declineInvite
|
|
summary: Decline a received invite
|
|
description: |
|
|
Transitions a `created` invite to `declined`. No notification is
|
|
published in v1.
|
|
|
|
Declined users may receive a new invite from the owner while enrollment
|
|
is open.
|
|
|
|
Authorization: the invited user (invitee_user_id must match X-User-ID).
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/InviteIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Invite record with status declined.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/InviteRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/invites/{invite_id}/revoke:
|
|
post:
|
|
tags:
|
|
- Invites
|
|
operationId: revokeInvite
|
|
summary: Revoke a sent invite
|
|
description: |
|
|
Transitions a `created` invite to `revoked`. No notification is
|
|
published in v1.
|
|
|
|
Authorization: private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/InviteIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Invite record with status revoked.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/InviteRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/memberships:
|
|
get:
|
|
tags:
|
|
- Memberships
|
|
operationId: listMemberships
|
|
summary: List memberships of a game
|
|
description: |
|
|
Returns a paginated list of memberships for the game.
|
|
|
|
Authorization: system administrator, game owner, or active member.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
- $ref: "#/components/parameters/PageSize"
|
|
- $ref: "#/components/parameters/PageToken"
|
|
responses:
|
|
"200":
|
|
description: One deterministic page of membership records.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/MembershipListResponse"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/memberships/{membership_id}/remove:
|
|
post:
|
|
tags:
|
|
- Memberships
|
|
operationId: removeMember
|
|
summary: Remove a member from a game
|
|
description: |
|
|
Removes an active member.
|
|
|
|
Before game start: drops the membership and releases the race name
|
|
reservation.
|
|
After game start: marks membership `removed`; `Game Master` must
|
|
deactivate the player slot; race name reservation is retained until
|
|
the game finishes.
|
|
|
|
Authorization: system administrator or private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/MembershipIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Updated membership record with status removed.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/MembershipRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/games/{game_id}/memberships/{membership_id}/block:
|
|
post:
|
|
tags:
|
|
- Memberships
|
|
operationId: blockMember
|
|
summary: Apply a platform-level block to a member
|
|
description: |
|
|
Blocks an active member. The engine slot is retained but the member
|
|
cannot send commands through `Game Master`. Race name reservation is
|
|
preserved.
|
|
|
|
Authorization: system administrator or private-game owner.
|
|
parameters:
|
|
- $ref: "#/components/parameters/GameIDPath"
|
|
- $ref: "#/components/parameters/MembershipIDPath"
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Updated membership record with status blocked.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/MembershipRecord"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"409":
|
|
$ref: "#/components/responses/ConflictError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/my/games:
|
|
get:
|
|
tags:
|
|
- MyLists
|
|
operationId: listMyGames
|
|
summary: List active games for the authenticated user
|
|
description: |
|
|
Returns games where the authenticated user holds an active membership
|
|
and the game status is `running` or `paused`. Response includes the
|
|
denormalized runtime snapshot for each game.
|
|
parameters:
|
|
- $ref: "#/components/parameters/XUserID"
|
|
- $ref: "#/components/parameters/PageSize"
|
|
- $ref: "#/components/parameters/PageToken"
|
|
responses:
|
|
"200":
|
|
description: One page of active game records including runtime snapshot.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GameListResponse"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/my/applications:
|
|
get:
|
|
tags:
|
|
- MyLists
|
|
operationId: listMyApplications
|
|
summary: List pending applications for the authenticated user
|
|
description: |
|
|
Returns applications submitted by the authenticated user with status
|
|
`submitted`. Each item includes game name and type for display.
|
|
parameters:
|
|
- $ref: "#/components/parameters/XUserID"
|
|
- $ref: "#/components/parameters/PageSize"
|
|
- $ref: "#/components/parameters/PageToken"
|
|
responses:
|
|
"200":
|
|
description: One page of submitted application items.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/MyApplicationListResponse"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/my/invites:
|
|
get:
|
|
tags:
|
|
- MyLists
|
|
operationId: listMyInvites
|
|
summary: List open invites addressed to the authenticated user
|
|
description: |
|
|
Returns invites addressed to the authenticated user with status
|
|
`created`. Each item includes game name, inviter name, and `expires_at`.
|
|
parameters:
|
|
- $ref: "#/components/parameters/XUserID"
|
|
- $ref: "#/components/parameters/PageSize"
|
|
- $ref: "#/components/parameters/PageToken"
|
|
responses:
|
|
"200":
|
|
description: One page of open invite items.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/MyInviteListResponse"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/my/race-names:
|
|
get:
|
|
tags:
|
|
- RaceNames
|
|
operationId: listMyRaceNames
|
|
summary: List the acting user's race-name directory entries
|
|
description: |
|
|
Returns the acting user's view of the Race Name Directory across
|
|
all three levels of binding: permanent registered names,
|
|
`pending_registration` entries waiting for the 30-day window to
|
|
elapse, and active per-game reservations. Each reservation
|
|
carries the current `game_status` of its hosting game so the UI
|
|
can render it next to the game state. The endpoint reads only
|
|
the `user_registered` and `user_reservations` indexes; it never
|
|
scans the full directory.
|
|
|
|
The response is exclusively scoped to the caller. There is no
|
|
`?user_id=` parameter; admin-side cross-user reads are not
|
|
exposed by this route.
|
|
parameters:
|
|
- $ref: "#/components/parameters/XUserID"
|
|
responses:
|
|
"200":
|
|
description: Snapshot of the acting user's race-name bindings.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/MyRaceNamesResponse"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
/api/v1/lobby/race-names/register:
|
|
post:
|
|
tags:
|
|
- RaceNames
|
|
operationId: registerRaceName
|
|
summary: Convert a pending race-name registration into a permanent one
|
|
description: |
|
|
Converts the caller's `pending_registration` for
|
|
`(source_game_id, race_name)` into a permanent registered race
|
|
name. The pending entry must still be inside its 30-day window,
|
|
the caller must not carry an active `permanent_block`, and the
|
|
caller's `max_registered_race_names` allowance from the User
|
|
Service eligibility snapshot must permit the new registration
|
|
(a value of `0` denotes the unlimited lifetime tariff).
|
|
|
|
The call is idempotent: a repeated request with the same body
|
|
returns the previously registered record without consuming any
|
|
additional quota slot. The notification intent
|
|
`lobby.race_name.registered` is emitted on every successful
|
|
return; consumers deduplicate using the stable idempotency key
|
|
`lobby.race_name.registered:<source_game_id>:<user_id>`.
|
|
parameters:
|
|
- $ref: "#/components/parameters/XUserID"
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/RegisterRaceNameRequest"
|
|
responses:
|
|
"200":
|
|
description: Race name successfully registered.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/RegisteredRaceName"
|
|
"400":
|
|
$ref: "#/components/responses/InvalidRequestError"
|
|
"403":
|
|
$ref: "#/components/responses/ForbiddenError"
|
|
"404":
|
|
$ref: "#/components/responses/NotFoundError"
|
|
"422":
|
|
$ref: "#/components/responses/DomainPreconditionError"
|
|
"500":
|
|
$ref: "#/components/responses/InternalError"
|
|
"503":
|
|
$ref: "#/components/responses/ServiceUnavailableError"
|
|
components:
|
|
parameters:
|
|
XUserID:
|
|
name: X-User-ID
|
|
in: header
|
|
required: true
|
|
description: |
|
|
Authenticated platform user identifier injected by `Edge Gateway`.
|
|
`Lobby` derives the acting user identity exclusively from this header.
|
|
schema:
|
|
type: string
|
|
GameIDPath:
|
|
name: game_id
|
|
in: path
|
|
required: true
|
|
description: Opaque stable game identifier.
|
|
schema:
|
|
type: string
|
|
ApplicationIDPath:
|
|
name: application_id
|
|
in: path
|
|
required: true
|
|
description: Opaque stable application identifier.
|
|
schema:
|
|
type: string
|
|
InviteIDPath:
|
|
name: invite_id
|
|
in: path
|
|
required: true
|
|
description: Opaque stable invite identifier.
|
|
schema:
|
|
type: string
|
|
MembershipIDPath:
|
|
name: membership_id
|
|
in: path
|
|
required: true
|
|
description: Opaque stable membership identifier.
|
|
schema:
|
|
type: string
|
|
PageSize:
|
|
name: page_size
|
|
in: query
|
|
required: false
|
|
description: |
|
|
Maximum number of items to return. Default is `50`; maximum is `200`.
|
|
schema:
|
|
type: integer
|
|
minimum: 1
|
|
maximum: 200
|
|
default: 50
|
|
PageToken:
|
|
name: page_token
|
|
in: query
|
|
required: false
|
|
description: Opaque continuation token returned as `next_page_token` in a previous response.
|
|
schema:
|
|
type: string
|
|
schemas:
|
|
GameRecord:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- game_id
|
|
- game_name
|
|
- game_type
|
|
- owner_user_id
|
|
- status
|
|
- min_players
|
|
- max_players
|
|
- start_gap_hours
|
|
- start_gap_players
|
|
- enrollment_ends_at
|
|
- turn_schedule
|
|
- target_engine_version
|
|
- created_at
|
|
- updated_at
|
|
- current_turn
|
|
- runtime_status
|
|
- engine_health_summary
|
|
properties:
|
|
game_id:
|
|
type: string
|
|
description: Opaque stable game identifier in game-* form.
|
|
game_name:
|
|
type: string
|
|
description: Human-readable game name; mutable in draft status.
|
|
description:
|
|
type: string
|
|
description: Optional game description; mutable in draft and enrollment_open.
|
|
game_type:
|
|
type: string
|
|
enum:
|
|
- public
|
|
- private
|
|
description: Game visibility and enrollment model.
|
|
owner_user_id:
|
|
type: string
|
|
description: Platform user identifier of the private-game owner; empty for public games.
|
|
status:
|
|
type: string
|
|
enum:
|
|
- draft
|
|
- enrollment_open
|
|
- ready_to_start
|
|
- starting
|
|
- start_failed
|
|
- running
|
|
- paused
|
|
- finished
|
|
- cancelled
|
|
description: Current platform-level lifecycle status.
|
|
min_players:
|
|
type: integer
|
|
description: Minimum approved participants required to proceed to start.
|
|
max_players:
|
|
type: integer
|
|
description: Target roster size that activates the gap window.
|
|
start_gap_hours:
|
|
type: integer
|
|
description: Hours of gap window after max_players is reached.
|
|
start_gap_players:
|
|
type: integer
|
|
description: Additional participants admitted during the gap window.
|
|
enrollment_ends_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix seconds; deadline for automatic enrollment close.
|
|
turn_schedule:
|
|
type: string
|
|
description: Five-field cron expression for scheduled turn generation; passed to Game Master at registration.
|
|
target_engine_version:
|
|
type: string
|
|
description: Semver of the game engine to launch; passed to Game Master at registration.
|
|
created_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; record creation timestamp.
|
|
updated_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; last mutation timestamp.
|
|
started_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; set when status becomes running.
|
|
finished_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; set when status becomes finished.
|
|
current_turn:
|
|
type: integer
|
|
description: Denormalized from Game Master; zero until the game is running.
|
|
runtime_status:
|
|
type: string
|
|
description: Denormalized from Game Master; empty until the game is running.
|
|
engine_health_summary:
|
|
type: string
|
|
description: Denormalized from Game Master; empty until the game is running.
|
|
runtime_binding:
|
|
$ref: "#/components/schemas/RuntimeBinding"
|
|
RuntimeBinding:
|
|
type: object
|
|
additionalProperties: false
|
|
description: |
|
|
Runtime binding metadata persisted on the game record after a
|
|
successful container start. Absent before the start sequence
|
|
completes.
|
|
required:
|
|
- container_id
|
|
- engine_endpoint
|
|
- runtime_job_id
|
|
- bound_at
|
|
properties:
|
|
container_id:
|
|
type: string
|
|
description: Engine container identifier assigned by Runtime Manager.
|
|
engine_endpoint:
|
|
type: string
|
|
description: Network address Game Master uses to reach the engine container.
|
|
runtime_job_id:
|
|
type: string
|
|
description: |
|
|
Source `runtime:job_results` Redis Stream message id (in `<ms>-<seq>`
|
|
form) that produced this binding.
|
|
bound_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds when the binding was persisted.
|
|
ApplicationRecord:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- application_id
|
|
- game_id
|
|
- applicant_user_id
|
|
- race_name
|
|
- status
|
|
- created_at
|
|
properties:
|
|
application_id:
|
|
type: string
|
|
description: Opaque stable application identifier.
|
|
game_id:
|
|
type: string
|
|
description: Identifier of the game this application belongs to.
|
|
applicant_user_id:
|
|
type: string
|
|
description: Platform user identifier of the applicant.
|
|
race_name:
|
|
type: string
|
|
description: Desired in-game name submitted with the application.
|
|
status:
|
|
type: string
|
|
enum:
|
|
- submitted
|
|
- approved
|
|
- rejected
|
|
description: Current application lifecycle status.
|
|
created_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; application submission timestamp.
|
|
decided_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; set when application is approved or rejected.
|
|
InviteRecord:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- invite_id
|
|
- game_id
|
|
- inviter_user_id
|
|
- invitee_user_id
|
|
- status
|
|
- created_at
|
|
- expires_at
|
|
properties:
|
|
invite_id:
|
|
type: string
|
|
description: Opaque stable invite identifier.
|
|
game_id:
|
|
type: string
|
|
description: Identifier of the game this invite belongs to.
|
|
inviter_user_id:
|
|
type: string
|
|
description: Platform user identifier of the game owner who created the invite.
|
|
invitee_user_id:
|
|
type: string
|
|
description: Platform user identifier of the invited user.
|
|
race_name:
|
|
type: string
|
|
description: In-game name chosen by the invitee at redeem time; absent until the invite is redeemed.
|
|
status:
|
|
type: string
|
|
enum:
|
|
- created
|
|
- redeemed
|
|
- declined
|
|
- revoked
|
|
- expired
|
|
description: Current invite lifecycle status.
|
|
created_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; invite creation timestamp.
|
|
expires_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; equals enrollment_ends_at of the game at creation time.
|
|
decided_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; set when invite is redeemed, declined, revoked, or expired.
|
|
MembershipRecord:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- membership_id
|
|
- game_id
|
|
- user_id
|
|
- race_name
|
|
- status
|
|
- joined_at
|
|
properties:
|
|
membership_id:
|
|
type: string
|
|
description: Opaque stable membership identifier.
|
|
game_id:
|
|
type: string
|
|
description: Identifier of the game this membership belongs to.
|
|
user_id:
|
|
type: string
|
|
description: Platform user identifier of the member.
|
|
race_name:
|
|
type: string
|
|
description: Confirmed in-game name; reserved in Race Name Directory.
|
|
status:
|
|
type: string
|
|
enum:
|
|
- active
|
|
- removed
|
|
- blocked
|
|
description: Current membership status.
|
|
joined_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; membership activation timestamp.
|
|
removed_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix milliseconds; set when membership is removed or blocked.
|
|
MyApplicationItem:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- application_id
|
|
- game_id
|
|
- applicant_user_id
|
|
- race_name
|
|
- status
|
|
- created_at
|
|
- game_name
|
|
- game_type
|
|
properties:
|
|
application_id:
|
|
type: string
|
|
game_id:
|
|
type: string
|
|
applicant_user_id:
|
|
type: string
|
|
race_name:
|
|
type: string
|
|
status:
|
|
type: string
|
|
enum:
|
|
- submitted
|
|
- approved
|
|
- rejected
|
|
created_at:
|
|
type: integer
|
|
format: int64
|
|
decided_at:
|
|
type: integer
|
|
format: int64
|
|
game_name:
|
|
type: string
|
|
description: Human-readable game name for display purposes.
|
|
game_type:
|
|
type: string
|
|
enum:
|
|
- public
|
|
- private
|
|
description: Game type for display purposes.
|
|
MyInviteItem:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- invite_id
|
|
- game_id
|
|
- inviter_user_id
|
|
- invitee_user_id
|
|
- status
|
|
- created_at
|
|
- expires_at
|
|
- game_name
|
|
- inviter_name
|
|
properties:
|
|
invite_id:
|
|
type: string
|
|
game_id:
|
|
type: string
|
|
inviter_user_id:
|
|
type: string
|
|
invitee_user_id:
|
|
type: string
|
|
race_name:
|
|
type: string
|
|
status:
|
|
type: string
|
|
enum:
|
|
- created
|
|
- redeemed
|
|
- declined
|
|
- revoked
|
|
- expired
|
|
created_at:
|
|
type: integer
|
|
format: int64
|
|
expires_at:
|
|
type: integer
|
|
format: int64
|
|
decided_at:
|
|
type: integer
|
|
format: int64
|
|
game_name:
|
|
type: string
|
|
description: Human-readable game name for display purposes.
|
|
inviter_name:
|
|
type: string
|
|
description: Owner's race name if already a member of the game; otherwise the owner's user_id.
|
|
CreateGameRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- game_name
|
|
- game_type
|
|
- min_players
|
|
- max_players
|
|
- start_gap_hours
|
|
- start_gap_players
|
|
- enrollment_ends_at
|
|
- turn_schedule
|
|
- target_engine_version
|
|
properties:
|
|
game_name:
|
|
type: string
|
|
description: Human-readable game name; must be non-empty after trim.
|
|
description:
|
|
type: string
|
|
description: Optional game description.
|
|
game_type:
|
|
type: string
|
|
enum:
|
|
- public
|
|
- private
|
|
description: Game visibility and enrollment model.
|
|
min_players:
|
|
type: integer
|
|
minimum: 1
|
|
description: Minimum approved participants required to proceed to start; must be <= max_players.
|
|
max_players:
|
|
type: integer
|
|
minimum: 1
|
|
description: Target roster size that activates the gap window; must be >= min_players.
|
|
start_gap_hours:
|
|
type: integer
|
|
minimum: 0
|
|
description: Hours of gap window after max_players is reached.
|
|
start_gap_players:
|
|
type: integer
|
|
minimum: 0
|
|
description: Additional participants admitted during the gap window.
|
|
enrollment_ends_at:
|
|
type: integer
|
|
format: int64
|
|
description: UTC Unix seconds; deadline for automatic enrollment close; must be a positive integer.
|
|
turn_schedule:
|
|
type: string
|
|
description: Valid five-field cron expression for scheduled turn generation.
|
|
target_engine_version:
|
|
type: string
|
|
description: Non-empty semver string of the game engine to launch.
|
|
UpdateGameRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
description: |
|
|
Partial update of a game record. Only fields present in the request body
|
|
are modified. `game_name`, `min_players`, `max_players`,
|
|
`start_gap_hours`, `start_gap_players`, `enrollment_ends_at`,
|
|
`turn_schedule`, and `target_engine_version` are mutable in `draft`
|
|
status only. `description` is additionally mutable in `enrollment_open`
|
|
status.
|
|
properties:
|
|
game_name:
|
|
type: string
|
|
description:
|
|
type: string
|
|
min_players:
|
|
type: integer
|
|
minimum: 1
|
|
max_players:
|
|
type: integer
|
|
minimum: 1
|
|
start_gap_hours:
|
|
type: integer
|
|
minimum: 0
|
|
start_gap_players:
|
|
type: integer
|
|
minimum: 0
|
|
enrollment_ends_at:
|
|
type: integer
|
|
format: int64
|
|
turn_schedule:
|
|
type: string
|
|
target_engine_version:
|
|
type: string
|
|
SubmitApplicationRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- race_name
|
|
properties:
|
|
race_name:
|
|
type: string
|
|
description: Desired in-game name; must be available in the Race Name Directory.
|
|
CreateInviteRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- invitee_user_id
|
|
properties:
|
|
invitee_user_id:
|
|
type: string
|
|
description: Platform user identifier of the user to invite.
|
|
RedeemInviteRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- race_name
|
|
properties:
|
|
race_name:
|
|
type: string
|
|
description: Desired in-game name; must be available in the Race Name Directory.
|
|
RegisterRaceNameRequest:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- race_name
|
|
- source_game_id
|
|
properties:
|
|
race_name:
|
|
type: string
|
|
description: |
|
|
Original-casing race name to register. Must match the
|
|
canonical key of an existing `pending_registration` owned by
|
|
the caller in `source_game_id`.
|
|
source_game_id:
|
|
type: string
|
|
description: |
|
|
Identifier of the finished game whose capable finish
|
|
produced the pending registration to convert.
|
|
RegisteredRaceName:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- canonical_key
|
|
- race_name
|
|
- source_game_id
|
|
- registered_at_ms
|
|
properties:
|
|
canonical_key:
|
|
type: string
|
|
description: |
|
|
Race Name Directory canonical key derived from the policy
|
|
(lowercase + frozen confusable-pair map).
|
|
race_name:
|
|
type: string
|
|
description: Original-casing display value owned by the caller.
|
|
source_game_id:
|
|
type: string
|
|
description: |
|
|
Game whose capable finish produced the pending registration
|
|
converted by this call.
|
|
registered_at_ms:
|
|
type: integer
|
|
format: int64
|
|
description: |
|
|
UTC Unix milliseconds timestamp recorded by the directory
|
|
on the original commit. Idempotent retries return the same
|
|
value.
|
|
PendingRaceName:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- canonical_key
|
|
- race_name
|
|
- source_game_id
|
|
- eligible_until_ms
|
|
properties:
|
|
canonical_key:
|
|
type: string
|
|
description: |
|
|
Race Name Directory canonical key derived from the policy
|
|
(lowercase + frozen confusable-pair map).
|
|
race_name:
|
|
type: string
|
|
description: Original-casing display value held by the caller.
|
|
source_game_id:
|
|
type: string
|
|
description: |
|
|
Game whose capable finish produced this pending entry.
|
|
Use this value as `source_game_id` when calling
|
|
`lobby.race_name.register`.
|
|
reserved_at_ms:
|
|
type: integer
|
|
format: int64
|
|
description: |
|
|
UTC Unix milliseconds timestamp of the original `Reserve`
|
|
call that became this pending entry.
|
|
eligible_until_ms:
|
|
type: integer
|
|
format: int64
|
|
description: |
|
|
UTC Unix milliseconds deadline for converting the pending
|
|
entry into a registered race name. After this moment the
|
|
pending-registration expiration worker releases it.
|
|
RaceNameReservation:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- canonical_key
|
|
- race_name
|
|
- game_id
|
|
- game_status
|
|
properties:
|
|
canonical_key:
|
|
type: string
|
|
description: |
|
|
Race Name Directory canonical key derived from the policy
|
|
(lowercase + frozen confusable-pair map).
|
|
race_name:
|
|
type: string
|
|
description: Original-casing display value held by the caller.
|
|
game_id:
|
|
type: string
|
|
description: Game hosting the reservation.
|
|
reserved_at_ms:
|
|
type: integer
|
|
format: int64
|
|
description: |
|
|
UTC Unix milliseconds timestamp of the `Reserve` call.
|
|
game_status:
|
|
type: string
|
|
description: |
|
|
Current `game.Status` of the hosting game. Empty when the
|
|
game record cannot be loaded (defensive only — this should
|
|
not occur in normal operation).
|
|
MyRaceNamesResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- registered
|
|
- pending
|
|
- reservations
|
|
properties:
|
|
registered:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/RegisteredRaceName"
|
|
pending:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/PendingRaceName"
|
|
reservations:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/RaceNameReservation"
|
|
GameListResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- items
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/GameRecord"
|
|
next_page_token:
|
|
type: string
|
|
description: Opaque continuation token; absent when no further pages exist.
|
|
MembershipListResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- items
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/MembershipRecord"
|
|
next_page_token:
|
|
type: string
|
|
description: Opaque continuation token; absent when no further pages exist.
|
|
MyApplicationListResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- items
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/MyApplicationItem"
|
|
next_page_token:
|
|
type: string
|
|
description: Opaque continuation token; absent when no further pages exist.
|
|
MyInviteListResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- items
|
|
properties:
|
|
items:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/MyInviteItem"
|
|
next_page_token:
|
|
type: string
|
|
description: Opaque continuation token; absent when no further pages exist.
|
|
ProbeResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- status
|
|
properties:
|
|
status:
|
|
type: string
|
|
description: Stable probe outcome string.
|
|
ErrorResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- error
|
|
properties:
|
|
error:
|
|
$ref: "#/components/schemas/ErrorBody"
|
|
ErrorBody:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- code
|
|
- message
|
|
properties:
|
|
code:
|
|
type: string
|
|
description: Stable internal API error code.
|
|
message:
|
|
type: string
|
|
description: Human-readable trusted error message.
|
|
responses:
|
|
InvalidRequestError:
|
|
description: Request validation failed.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ErrorResponse"
|
|
examples:
|
|
invalidRequest:
|
|
value:
|
|
error:
|
|
code: invalid_request
|
|
message: request is invalid
|
|
ForbiddenError:
|
|
description: Caller is not authorized for this operation on this resource.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ErrorResponse"
|
|
examples:
|
|
forbidden:
|
|
value:
|
|
error:
|
|
code: forbidden
|
|
message: access denied
|
|
NotFoundError:
|
|
description: The requested game, application, invite, or membership does not exist.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ErrorResponse"
|
|
examples:
|
|
notFound:
|
|
value:
|
|
error:
|
|
code: subject_not_found
|
|
message: resource not found
|
|
ConflictError:
|
|
description: The requested state transition is not allowed from the current status.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ErrorResponse"
|
|
examples:
|
|
conflict:
|
|
value:
|
|
error:
|
|
code: conflict
|
|
message: operation not allowed in current status
|
|
DomainPreconditionError:
|
|
description: |
|
|
A domain-level precondition was not met. Stable codes returned under
|
|
this response:
|
|
- `eligibility_denied` — user not eligible per User Service
|
|
- `name_taken` — race_name is already reserved by another user
|
|
- `race_name_pending_window_expired` — the 30-day pending
|
|
registration window has lapsed
|
|
- `race_name_registration_quota_exceeded` — caller exhausted their
|
|
tariff `max_registered_race_names` allowance
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ErrorResponse"
|
|
examples:
|
|
eligibilityDenied:
|
|
value:
|
|
error:
|
|
code: eligibility_denied
|
|
message: user is not eligible to join games
|
|
nameTaken:
|
|
value:
|
|
error:
|
|
code: name_taken
|
|
message: race name is already taken
|
|
raceNamePendingWindowExpired:
|
|
value:
|
|
error:
|
|
code: race_name_pending_window_expired
|
|
message: pending race-name registration window has expired
|
|
raceNameRegistrationQuotaExceeded:
|
|
value:
|
|
error:
|
|
code: race_name_registration_quota_exceeded
|
|
message: race name registration quota exceeded
|
|
InternalError:
|
|
description: Unexpected internal service error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ErrorResponse"
|
|
examples:
|
|
internal:
|
|
value:
|
|
error:
|
|
code: internal_error
|
|
message: internal server error
|
|
ServiceUnavailableError:
|
|
description: An upstream dependency is unavailable.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/ErrorResponse"
|
|
examples:
|
|
unavailable:
|
|
value:
|
|
error:
|
|
code: service_unavailable
|
|
message: service is unavailable
|