Base URL: http://localhost:8000 (development) | https://your-domain.com (production)
API Version: v1
Protocol: REST (HTTP) + WebSocket (Socket.IO)
The platform uses cookie-based sessions for authentication:
- Cookie Name:
vbrpg_session - Cookie Attributes: HttpOnly, SameSite=Lax, Max-Age=30 days
- Session ID Format: 32-character hexadecimal string
Sessions are created automatically when:
- Creating a guest account
- Registering a new account
- Logging in with existing credentials
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
Health check endpoint.
Response (200 OK):
{
"status": "healthy",
"timestamp": "2025-11-08T12:00:00Z",
"version": "1.0.0"
}System metrics (Prometheus format).
Response (200 OK):
# HELP active_games Number of active game sessions
# TYPE active_games gauge
active_games 42
# HELP total_players Total connected players
# TYPE total_players gauge
total_players 156
Create a new guest player account.
Request Body: None required
Response (201 Created):
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "Guest_快乐_熊猫",
"is_guest": true,
"session_id": "a1b2c3d4e5f6...",
"created_at": "2025-11-08T12:00:00Z"
}Set-Cookie Header:
vbrpg_session=a1b2c3d4e5f6...; HttpOnly; SameSite=Lax; Max-Age=2592000; Path=/
Error Responses:
500- Server error creating guest account
Register a new player account (upgrade from guest or create new).
Request Body:
{
"username": "player123",
"email": "player@example.com",
"password": "SecurePassword123!"
}Validation Rules:
username: 3-20 characters, alphanumeric + underscore + Chinese charactersemail: Valid email formatpassword: Minimum 8 characters
Response (201 Created):
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player123",
"email": "player@example.com",
"is_guest": false,
"created_at": "2025-11-08T12:00:00Z"
}Error Responses:
400- Validation error (invalid username/email/password)409- Username or email already exists
Get current player's profile.
Authentication: Required (session cookie)
Response (200 OK):
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player123",
"email": "player@example.com",
"is_guest": false,
"total_games": 15,
"total_wins": 8,
"created_at": "2025-11-01T10:00:00Z",
"last_seen": "2025-11-08T12:00:00Z"
}Error Responses:
401- Not authenticated
Get player profile by ID.
Path Parameters:
player_id: UUID of the player
Response (200 OK):
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player123",
"total_games": 15,
"total_wins": 8,
"created_at": "2025-11-01T10:00:00Z"
}Note: Email and sensitive data excluded from public profile
Error Responses:
404- Player not found
Create a new game room.
Authentication: Required (session cookie)
Request Body:
{
"max_players": 4,
"difficulty": "medium",
"turn_time_limit": 60,
"use_ai_narrator": true
}Validation Rules:
max_players: 2-6difficulty: "easy" | "medium" | "hard"turn_time_limit: 30-300 secondsuse_ai_narrator: boolean
Response (201 Created):
{
"room_id": "660e8400-e29b-41d4-a716-446655440001",
"room_code": "ABC123",
"creator_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "waiting",
"max_players": 4,
"current_players": 1,
"difficulty": "medium",
"turn_time_limit": 60,
"use_ai_narrator": true,
"created_at": "2025-11-08T12:00:00Z"
}Error Responses:
400- Invalid room configuration401- Not authenticated
Join an existing game room.
Authentication: Required (session cookie)
Request Body:
{
"room_code": "ABC123"
}Response (200 OK):
{
"room_id": "660e8400-e29b-41d4-a716-446655440001",
"room_code": "ABC123",
"status": "waiting",
"max_players": 4,
"current_players": 2,
"players": [
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player123",
"is_creator": true
},
{
"player_id": "550e8400-e29b-41d4-a716-446655440002",
"username": "player456",
"is_creator": false
}
]
}Error Responses:
400- Invalid room code format404- Room not found409- Room is full or game already started
Join an existing game room using the room code path parameter (Feature 002).
Authentication: Required (session cookie)
Path Parameters:
code: 6-character alphanumeric room code (case-insensitive)
Request Body:
{
"player_id": "550e8400-e29b-41d4-a716-446655440000"
}Response (200 OK):
{
"room": {
"id": 42,
"code": "ABC123",
"game_type_id": 1,
"status": "Waiting",
"max_players": 4,
"current_participant_count": 2,
"owner_id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2025-11-08T12:00:00Z",
"started_at": null,
"completed_at": null
},
"participants": [
{
"id": 1,
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"player_name": "Alice",
"avatar_url": "https://example.com/avatars/alice.png",
"participant_type": "human",
"is_owner": true,
"join_timestamp": "2025-11-08T12:00:00Z"
},
{
"id": 2,
"player_id": "660e9500-f30c-52e5-b827-557766551111",
"player_name": "Bob",
"avatar_url": null,
"participant_type": "human",
"is_owner": false,
"join_timestamp": "2025-11-08T12:01:00Z"
}
],
"is_owner": false
}Error Responses:
400- Invalid room code format or duplicate join{ "error": "INVALID_ROOM_CODE", "message": "Room code must be 6 alphanumeric characters" }{ "error": "DUPLICATE_JOIN", "message": "Player already in this room" }404- Room not found{ "error": "ROOM_NOT_FOUND", "message": "No room exists with code ABC123" }409- Room is full or game already started{ "error": "ROOM_FULL", "message": "This room is at maximum capacity (4/4 players)" }{ "error": "GAME_ALREADY_STARTED", "message": "Cannot join room - game is already in progress" }
Leave a game room (Feature 002).
Authentication: Required (session cookie)
Path Parameters:
code: 6-character alphanumeric room codeplayer_id: UUID of the player leaving
Behavior:
- If the player is the owner, ownership transfers to the next earliest-joined human player
- If only AI agents remain after owner leaves, the room is dissolved
- Cannot leave while game is in progress
Response (204 No Content): Empty response body on success
Error Responses:
404- Room or player not found{ "error": "ROOM_NOT_FOUND", "message": "No room exists with code ABC123" }{ "error": "PLAYER_NOT_IN_ROOM", "message": "Player is not a participant in this room" }409- Cannot leave while game is in progress{ "error": "GAME_IN_PROGRESS", "message": "Cannot leave room while game is active" }
Add an AI agent to the room (Feature 002, owner only).
Authentication: Required (must be room owner)
Path Parameters:
code: 6-character alphanumeric room code
Request Body:
{
"owner_player_id": "550e8400-e29b-41d4-a716-446655440000"
}Response (201 Created):
{
"ai_agent": {
"id": 3,
"player_id": "a1b2c3d4-e5f6-4g7h-8i9j-0k1l2m3n4o5p",
"player_name": "AI玩家1",
"avatar_url": null,
"participant_type": "ai",
"is_owner": false,
"join_timestamp": "2025-11-08T12:02:00Z"
},
"room_code": "ABC123"
}Error Responses:
403- Not the room owner{ "error": "NOT_ROOM_OWNER", "message": "Only the room owner can add AI agents" }404- Room not found409- Room is full or game already started{ "error": "ROOM_FULL", "message": "Cannot add AI agent - room is at maximum capacity" }{ "error": "GAME_ALREADY_STARTED", "message": "Cannot modify participants after game starts" }
Remove an AI agent from the room (Feature 002, owner only).
Authentication: Required (must be room owner)
Path Parameters:
code: 6-character alphanumeric room codeagent_id: UUID of the AI agent to remove
Request Body:
{
"owner_player_id": "550e8400-e29b-41d4-a716-446655440000"
}Response (204 No Content): Empty response body on success
Error Responses:
403- Not the room owner404- Room or AI agent not found{ "error": "AI_AGENT_NOT_FOUND", "message": "No AI agent with ID exists in this room" }409- Game already started
Get room details.
Path Parameters:
room_code: 6-character room code
Response (200 OK):
{
"room_id": "660e8400-e29b-41d4-a716-446655440001",
"room_code": "ABC123",
"status": "in_progress",
"max_players": 4,
"current_players": 4,
"difficulty": "medium",
"turn_time_limit": 60,
"use_ai_narrator": true,
"started_at": "2025-11-08T12:05:00Z"
}Error Responses:
404- Room not found
Start the game (room creator only).
Authentication: Required (must be room creator)
Path Parameters:
room_code: 6-character room code
Response (200 OK):
{
"room_id": "660e8400-e29b-41d4-a716-446655440001",
"status": "in_progress",
"started_at": "2025-11-08T12:05:00Z",
"game_state_id": "770e8400-e29b-41d4-a716-446655440003"
}Error Responses:
403- Not the room creator409- Not enough players (minimum 2 required)
URL: ws://localhost:8000/socket.io/
Protocol: Socket.IO
Authentication: Include session cookie in connection headers
Join a game room WebSocket channel.
Payload:
{
"room_code": "ABC123"
}Server Response: room_joined or error
Submit a player action during the game.
Payload:
{
"room_code": "ABC123",
"action": {
"type": "investigate",
"location": "library",
"details": "searching for clues"
}
}Action Types:
investigate: Examine a locationquestion: Ask another characteraccuse: Make an accusationmove: Move to a new locationexamine: Inspect an objectuse_item: Use an inventory itemspeak: General dialogue
Server Response: action_processed or error
Send a chat message to other players.
Payload:
{
"room_code": "ABC123",
"message": "I found something interesting!"
}Server Response: Broadcasts chat_message to all players in room
Confirmation of joining a room.
Payload:
{
"room_code": "ABC123",
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"players": [...]
}Another player joined the room.
Payload:
{
"player_id": "550e8400-e29b-41d4-a716-446655440002",
"username": "player456",
"current_players": 3
}Broadcast when a human player joins the room lobby.
Channel: lobby:{room_code}
Payload:
{
"room_code": "ABC123",
"player": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Alice",
"avatar_url": "https://example.com/avatars/alice.png",
"participant_type": "human",
"is_owner": false,
"join_timestamp": "2025-11-09T10:30:00Z"
},
"timestamp": "2025-11-09T10:30:00Z"
}A player left the room.
Payload:
{
"player_id": "550e8400-e29b-41d4-a716-446655440002",
"current_players": 2
}Broadcast when a player leaves the room lobby.
Channel: lobby:{room_code}
Payload:
{
"room_code": "ABC123",
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"player_name": "Alice",
"timestamp": "2025-11-09T10:35:00Z"
}Broadcast when the room owner manually adds an AI agent.
Channel: lobby:{room_code}
Payload:
{
"room_code": "ABC123",
"ai_agent": {
"id": "a1b2c3d4-e5f6-4g7h-8i9j-0k1l2m3n4o5p",
"name": "AI玩家1",
"participant_type": "ai",
"is_owner": false,
"join_timestamp": "2025-11-09T10:32:00Z"
},
"added_by": "550e8400-e29b-41d4-a716-446655440000",
"added_by_name": "Bob",
"timestamp": "2025-11-09T10:32:00Z"
}Notes:
- AI agents are auto-named sequentially (AI玩家1, AI玩家2, etc.)
is_owneris alwaysfalsefor AI agents- Only room owner can add AI agents
Broadcast when the room owner removes an AI agent.
Channel: lobby:{room_code}
Payload:
{
"room_code": "ABC123",
"ai_agent_id": "a1b2c3d4-e5f6-4g7h-8i9j-0k1l2m3n4o5p",
"ai_agent_name": "AI玩家1",
"removed_by": "550e8400-e29b-41d4-a716-446655440000",
"removed_by_name": "Bob",
"timestamp": "2025-11-09T10:33:00Z"
}Broadcast when room ownership changes (owner leaves or manual transfer).
Channel: lobby:{room_code}
Payload:
{
"room_code": "ABC123",
"previous_owner_id": "550e8400-e29b-41d4-a716-446655440000",
"previous_owner_name": "Bob",
"new_owner_id": "660e9500-f30c-52e5-b827-557766551111",
"new_owner_name": "Charlie",
"reason": "owner_left",
"timestamp": "2025-11-09T10:36:00Z"
}Reason Values:
owner_left- Previous owner left the roommanual_transfer- Owner manually transferred ownership
Notes:
- Ownership transfers to earliest-joined human player
- AI agents are never eligible to become room owners
- New owner gains AI management controls
Broadcast when the room is dissolved and no longer exists.
Channel: lobby:{room_code}
Payload:
{
"room_code": "ABC123",
"reason": "no_human_players_remaining",
"timestamp": "2025-11-09T10:37:00Z"
}Reason Values:
no_human_players_remaining- Last human player left, only AI agents remainedtimeout- Room inactive for extended periodmanual_deletion- Admin or owner manually deleted room
Notes:
- All connected clients are disconnected from the lobby channel
- Room cannot be rejoined (returns 404)
- Game rooms with only AI agents cannot exist
The game has begun.
Payload:
{
"room_code": "ABC123",
"game_state_id": "770e8400-e29b-41d4-a716-446655440003",
"current_turn_player_id": "550e8400-e29b-41d4-a716-446655440000",
"turn_number": 1,
"phase": "investigation"
}Game state changed after an action.
Payload:
{
"game_state_id": "770e8400-e29b-41d4-a716-446655440003",
"turn_number": 5,
"phase": "investigation",
"current_turn_player_id": "550e8400-e29b-41d4-a716-446655440002",
"game_data": {
"locations": [...],
"characters": [...],
"clues_found": [...]
}
}Player's turn expired.
Payload:
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"turn_number": 5
}Game has ended.
Payload:
{
"room_code": "ABC123",
"winner_id": "550e8400-e29b-41d4-a716-446655440000",
"winner_username": "player123",
"final_state": {
"solution": "Colonel Mustard in the Library with the Candlestick",
"turns_taken": 25
}
}A disconnected player reconnected.
Payload:
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player123"
}Recoverable error occurred.
Payload:
{
"error_type": "llm_timeout",
"message": "AI response timed out, retrying...",
"recoverable": true,
"timestamp": "2025-11-08T12:10:00Z"
}Unrecoverable error, game terminated.
Payload:
{
"reason": "llm_failure",
"message": "Unable to connect to AI service, game terminated",
"terminated_at": "2025-11-08T12:15:00Z"
}Chat message from another player.
Payload:
{
"player_id": "550e8400-e29b-41d4-a716-446655440002",
"username": "player456",
"message": "Great find!",
"timestamp": "2025-11-08T12:10:00Z"
}{
player_id: string; // UUID
username: string; // 3-20 chars, alphanumeric + _ + Chinese
email?: string; // Only for registered accounts
is_guest: boolean;
total_games: number;
total_wins: number;
created_at: string; // ISO 8601
last_seen: string; // ISO 8601
}{
room_id: string; // UUID
room_code: string; // 6-char uppercase alphanumeric
creator_id: string; // UUID
status: "waiting" | "in_progress" | "completed";
max_players: number; // 2-6
current_players: number;
difficulty: "easy" | "medium" | "hard";
turn_time_limit: number; // seconds, 30-300
use_ai_narrator: boolean;
created_at: string;
started_at?: string;
completed_at?: string;
}{
type: "investigate" | "question" | "accuse" | "move" | "examine" | "use_item" | "speak";
location?: string; // For investigate, move
target?: string; // For question, accuse
question?: string; // For question
evidence?: string; // For accuse
object?: string; // For examine
item?: string; // For use_item
details?: string; // Additional context
}All API errors return:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": {
"field": "Additional context"
}
}
}200 OK: Successful request201 Created: Resource created successfully400 Bad Request: Invalid input/validation error401 Unauthorized: Not authenticated403 Forbidden: Authenticated but not authorized404 Not Found: Resource doesn't exist409 Conflict: Resource conflict (e.g., username taken, room full)429 Too Many Requests: Rate limit exceeded500 Internal Server Error: Server error503 Service Unavailable: Service temporarily unavailable
VALIDATION_ERROR: Input validation failedNOT_FOUND: Resource not foundUNAUTHORIZED: Authentication requiredFORBIDDEN: Insufficient permissionsCONFLICT: Resource conflictRATE_LIMIT_EXCEEDED: Too many requestsLLM_TIMEOUT: AI service timeoutLLM_ERROR: AI service errorDATABASE_ERROR: Database operation failed
Default rate limits:
- Authentication endpoints: 5 requests/minute per IP
- Room creation: 10 requests/hour per user
- API calls: 100 requests/minute per user
- WebSocket messages: 60 messages/minute per connection
Rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1699459200
API versioning through URL path:
- Current:
/api/...(implied v1) - Future:
/api/v2/...
Breaking changes will increment version number. Non-breaking changes (new fields, new endpoints) added to current version.
For API questions or issues:
- GitHub Issues: https://github.com/yourusername/vbrpg/issues
- API Docs: http://localhost:8000/docs