REST API Documentation¶
This guide covers the MAID v1 REST API for external integrations, including authentication, rate limiting, endpoints, and real-time event streaming.
Overview¶
The MAID REST API provides programmatic access to server data and administrative functions. It is designed for: - External tools and dashboards - Discord/IRC bots - Monitoring systems - Custom integrations
Base URL: /api/v1
OpenAPI Documentation:
- Swagger UI: /api/docs
- ReDoc: /api/redoc
- OpenAPI JSON: /api/openapi.json
Note: OpenAPI documentation is automatically generated by FastAPI based on the endpoint definitions, Pydantic models, and docstrings in the codebase. No separate OpenAPI specification file is maintained.
Authentication¶
The API uses API key authentication via the X-API-Key header.
Obtaining an API Key¶
API keys are generated through the CLI or admin API:
# Generate a new API key
uv run maid api generate-key --name "My Integration" --permissions READ_PUBLIC,READ_PLAYERS
# Output:
# API Key created successfully!
# Key ID: 550e8400-e29b-41d4-a716-446655440000
# Name: My Integration
# Permissions: READ_PUBLIC, READ_PLAYERS
#
# IMPORTANT: Save this key now - it cannot be retrieved later!
# API Key: maid_550e8400_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Using the API Key¶
Include the key in the X-API-Key header:
curl -X GET "http://localhost:8080/api/v1/players" \
-H "X-API-Key: maid_550e8400_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
API Key Format¶
Keys follow the format: maid_{key_id_prefix}_{secret}
maid_- Prefix for identification{key_id_prefix}- First 8 characters of the key UUID{secret}- 32-character secure random token
Permissions¶
| Permission | Description |
|---|---|
READ_PUBLIC |
Read public server information |
READ_PLAYERS |
Read player data |
WRITE_PLAYERS |
Modify player data |
READ_WORLD |
Read world data (rooms, NPCs, items) |
WRITE_WORLD |
Modify world data |
ADMIN |
Administrative actions |
Permission Combinations:
- READ_ALL = READ_PUBLIC + READ_PLAYERS + READ_WORLD
- WRITE_ALL = WRITE_PLAYERS + WRITE_WORLD
- FULL_ACCESS = All permissions
Rate Limiting¶
The API implements sliding window rate limiting.
Default Limits¶
- Per API Key: 60 requests/minute (configurable per key)
- Sliding window: 60 seconds
Rate Limit Headers¶
All responses include rate limit information:
| Header | Description |
|---|---|
X-RateLimit-Limit |
Maximum requests per window |
X-RateLimit-Remaining |
Remaining requests in window |
X-RateLimit-Reset |
Unix timestamp when limit resets |
Rate Limit Exceeded¶
When rate limited, the API returns:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Remaining: 0
{
"detail": "Rate limit exceeded"
}
Endpoints¶
Health Check¶
GET /api/v1/health
Check API health status. No authentication required.
Response:
{
"status": "ok",
"timestamp": "2024-01-15T10:30:00Z",
"version": "0.1.0",
"uptime_seconds": 86400
}
Example:
Players API¶
List Players¶
GET /api/v1/players
List players with pagination, filtering, and sorting.
Required Permission: READ_PLAYERS
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
page |
int | 1 | Page number (1-indexed) |
page_size |
int | 50 | Items per page (1-100) |
search |
str | None | Search by username or email |
status |
str | None | Filter by status (active, banned, suspended) |
role |
str | None | Filter by role |
online_only |
bool | false | Only show online players |
sort_by |
str | username | Sort field (username, created_at, last_login, status) |
sort_order |
str | asc | Sort order (asc, desc) |
Response:
{
"players": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player1",
"status": "active",
"role": "user",
"is_online": true,
"created_at": "2024-01-15T10:30:00Z",
"last_login": "2024-01-20T15:45:00Z"
}
],
"total": 150,
"page": 1,
"page_size": 50,
"has_next": true,
"has_previous": false
}
Example:
curl "http://localhost:8080/api/v1/players?online_only=true&page_size=10" \
-H "X-API-Key: maid_xxx..."
Get Player Details¶
GET /api/v1/players/{player_id}
Get detailed information about a specific player.
Required Permission: READ_PLAYERS
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player1",
"email": "player@example.com",
"status": "active",
"role": "user",
"is_online": true,
"created_at": "2024-01-15T10:30:00Z",
"last_login": "2024-01-20T15:45:00Z",
"login_attempts": 0,
"locked_until": null,
"current_character": "char-123",
"current_location": "Town Square",
"metadata": {}
}
Update Player¶
PATCH /api/v1/players/{player_id}
Update player account data.
Required Permission: WRITE_PLAYERS
Request Body:
Response:
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player1",
"updated_fields": ["email", "metadata"],
"success": true,
"message": "Updated 2 field(s)"
}
Kick Player¶
POST /api/v1/players/{player_id}/kick
Disconnect an active player session.
Required Permission: ADMIN
Request Body:
Response:
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player1",
"reason": "Server maintenance",
"success": true,
"message": "Player kicked successfully"
}
Ban Player¶
POST /api/v1/players/{player_id}/ban
Ban a player account.
Required Permission: ADMIN
Request Body:
Response:
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player1",
"reason": "Violation of community guidelines",
"banned_until": "2024-01-16T10:30:00Z",
"success": true,
"message": "Player banned successfully"
}
Unban Player¶
DELETE /api/v1/players/{player_id}/ban
Remove a ban from a player account.
Required Permission: ADMIN
Response:
{
"player_id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player1",
"success": true,
"message": "Player unbanned successfully"
}
World API¶
List Rooms¶
GET /api/v1/world/rooms
List all rooms with optional filtering.
Required Permission: READ_WORLD
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
area_id |
str | None | Filter by area ID |
offset |
int | 0 | Pagination offset |
limit |
int | 50 | Items per page (1-100) |
Response:
{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Town Square",
"area_id": "660e8400-e29b-41d4-a716-446655440001",
"area_name": "Town",
"entity_count": 5
}
],
"total": 500,
"offset": 0,
"limit": 50,
"has_more": true
}
Get Room Details¶
GET /api/v1/world/rooms/{room_id}
Get detailed information about a specific room.
Required Permission: READ_WORLD
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Town Square",
"description": "The central square of the town.",
"area_id": "660e8400-e29b-41d4-a716-446655440001",
"area_name": "Town",
"exits": [
{
"direction": "north",
"destination_id": "770e8400-e29b-41d4-a716-446655440002",
"destination_name": "Market Street",
"is_locked": false
}
],
"entity_count": 5,
"properties": {
"terrain": "cobblestone",
"light_level": "bright"
}
}
Get Room Contents¶
GET /api/v1/world/rooms/{room_id}/contents
Get the contents of a room (players, NPCs, items).
Required Permission: READ_WORLD
Response:
{
"room_id": "550e8400-e29b-41d4-a716-446655440000",
"room_name": "Town Square",
"players": [
{"id": "aaa...", "name": "Player1", "entity_type": "player", "description": ""}
],
"npcs": [
{"id": "bbb...", "name": "Guard", "entity_type": "npc", "description": "A town guard"}
],
"items": [
{"id": "ccc...", "name": "Sword", "entity_type": "item", "description": "A rusty sword"}
],
"total_count": 3
}
List NPCs¶
GET /api/v1/world/npcs
List all NPCs with optional filtering.
Required Permission: READ_WORLD
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
room_id |
str | Filter by room ID |
npc_type |
str | Filter by NPC type |
is_hostile |
bool | Filter by hostility |
offset |
int | Pagination offset |
limit |
int | Items per page |
List Items¶
GET /api/v1/world/items
List all items (on ground) with optional filtering.
Required Permission: READ_WORLD
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
room_id |
str | Filter by room ID |
item_type |
str | Filter by item type |
is_takeable |
bool | Filter by takeability |
offset |
int | Pagination offset |
limit |
int | Items per page |
List Areas¶
GET /api/v1/world/areas
List all areas/zones.
Required Permission: READ_WORLD
Statistics API¶
Server Statistics¶
GET /api/v1/stats/server
Get server statistics.
Required Permission: READ_PUBLIC
Response:
{
"uptime_seconds": 86400,
"tick_rate": 4.0,
"current_tick": 345600,
"connected_players": 42,
"memory_usage_mb": 256.5,
"cpu_usage_percent": null,
"version": "0.1.0",
"start_time": "2024-01-14T10:30:00Z"
}
Player Statistics¶
GET /api/v1/stats/players
Get player statistics.
Required Permission: READ_PLAYERS
Response:
{
"online_count": 42,
"peak_players_today": 67,
"peak_players_all_time": 150,
"new_registrations_today": 5,
"new_registrations_this_week": 28,
"total_accounts": 1234,
"active_sessions": 42
}
Economy Statistics¶
GET /api/v1/stats/economy
Get economy statistics.
Required Permission: READ_WORLD
Response:
{
"total_currency_in_circulation": 1500000,
"transactions_today": 342,
"transactions_this_week": 2156,
"average_player_wealth": 1216.5,
"median_player_wealth": 500,
"top_player_wealth": 50000,
"currency_name": "gold"
}
Combat Statistics¶
GET /api/v1/stats/combat
Get combat statistics.
Required Permission: READ_WORLD
Response:
{
"battles_today": 156,
"battles_this_week": 1089,
"player_deaths_today": 23,
"player_deaths_this_week": 178,
"mob_kills_today": 412,
"mob_kills_this_week": 2891,
"total_damage_dealt_today": 145678,
"total_damage_dealt_this_week": 1023456,
"pvp_battles_today": 12,
"pvp_battles_this_week": 67
}
Command Statistics (Admin Only)¶
GET /api/v1/stats/commands
Get command usage statistics.
Required Permission: ADMIN
Response:
{
"total_commands_executed": 50000,
"commands_executed_today": 1234,
"commands_executed_this_week": 8567,
"unique_commands_used": 45,
"most_used_commands": [
{
"command": "look",
"total_invocations": 15000,
"invocations_today": 500,
"invocations_this_week": 3200,
"average_execution_time_ms": 2.5,
"error_count": 0
}
],
"error_rate_percent": 0.5
}
AI Pricing API¶
Get AI Pricing¶
GET /admin/ai/pricing
Get AI provider pricing configuration.
Required Permission: ADMIN
Response:
{
"providers": [
{
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"input_cost_per_1k": 0.003,
"output_cost_per_1k": 0.015
}
]
}
Update AI Pricing¶
PUT /admin/ai/pricing
Update pricing for a model/provider.
Required Permission: ADMIN
Request Body:
{
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"input_cost_per_1k": 0.003,
"output_cost_per_1k": 0.015
}
Response:
Player Client Auth API¶
Authentication endpoints for player game clients (not to be confused with the API key auth used by external integrations).
Player Login¶
POST /api/v1/auth/login
Authenticate a player and receive JWT tokens.
Request Body:
Response (200):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600
}
Refresh Player Token¶
POST /api/v1/auth/refresh
Refresh a player's access token.
Request Body:
Response (200):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 3600
}
Player Logout¶
POST /api/v1/auth/logout
Revoke the current player token.
Headers:
Response (200):
Get Current Player¶
GET /api/v1/auth/me
Get information about the currently authenticated player.
Headers:
Response (200):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"username": "player1",
"email": "player@example.com",
"created_at": "2024-01-15T10:30:00Z",
"last_login": "2024-01-20T15:45:00Z"
}
List Player Characters¶
GET /api/v1/characters
List the authenticated player's characters.
Headers:
Response (200):
{
"characters": [
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Gandalf",
"class": "Wizard",
"level": 20
}
]
}
Create Character¶
POST /api/v1/characters
Create a new character for the authenticated player.
Headers:
Request Body:
Response (201):
{
"id": "770e8400-e29b-41d4-a716-446655440002",
"name": "Aragorn",
"class": "Ranger",
"level": 1,
"message": "Character created successfully"
}
Get Character Details¶
GET /api/v1/characters/{character_id}
Get detailed information about a specific character.
Headers:
Response (200):
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Gandalf",
"class": "Wizard",
"level": 20,
"stats": {},
"location": "Town Square"
}
Update Player Settings¶
PUT /api/v1/settings
Update the authenticated player's settings.
Headers:
Request Body:
Response (200):
Observability Endpoints¶
Operational observability endpoints served on a separate internal port (9090), not the main API port. These are intended for infrastructure tooling (Prometheus, Kubernetes, load balancers) and should not be exposed publicly.
Prometheus Metrics¶
GET http://localhost:9090/metrics
Prometheus metrics scrape endpoint. Returns metrics in Prometheus exposition format.
Example:
Health Check¶
GET http://localhost:9090/healthz
Basic health check. Returns 200 if the process is running.
Response (200):
Readiness Check¶
GET http://localhost:9090/readyz
Readiness check. Returns 200 when the server is ready to accept player traffic (all content packs loaded, network listeners active).
Response (200):
Liveness Check¶
GET http://localhost:9090/livez
Liveness check. Returns 200 when the tick loop is actively running.
Response (200):
Admin API¶
Broadcast Message¶
POST /api/v1/admin/broadcast
Broadcast a message to all connected players.
Required Permission: ADMIN
Request Body:
Response:
Shutdown Server¶
POST /api/v1/admin/shutdown
Initiate graceful server shutdown.
Required Permission: ADMIN
Request Body:
Response:
{
"success": true,
"message": "Shutdown scheduled: Scheduled maintenance",
"scheduled_at": "2024-01-15T10:35:00Z"
}
Reload Configuration¶
POST /api/v1/admin/reload-config
Reload server configuration from files.
Required Permission: ADMIN
Response:
{
"success": true,
"message": "Configuration reloaded successfully",
"reloaded_sections": ["engine", "world"]
}
List API Keys¶
GET /api/v1/admin/api-keys
List all API keys.
Required Permission: ADMIN
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
include_inactive |
bool | false | Include revoked keys |
Response:
{
"keys": [
{
"key_id": "550e8400-e29b-41d4-a716-446655440000",
"name": "My Integration",
"permissions": ["READ_PUBLIC", "READ_PLAYERS"],
"created_at": "2024-01-15T10:30:00Z",
"expires_at": null,
"last_used": "2024-01-20T15:45:00Z",
"created_by": "admin",
"is_active": true,
"rate_limit_rpm": 60
}
],
"total": 5
}
Create API Key¶
POST /api/v1/admin/api-keys
Generate a new API key.
Required Permission: ADMIN
Request Body:
{
"name": "Bot Integration",
"permissions": ["READ_PUBLIC", "READ_PLAYERS"],
"expires_in_days": 365,
"rate_limit_rpm": 120
}
Response (201):
{
"key_id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Bot Integration",
"permissions": ["READ_PUBLIC", "READ_PLAYERS"],
"created_at": "2024-01-15T10:30:00Z",
"expires_at": "2025-01-15T10:30:00Z",
"last_used": null,
"created_by": "admin-key",
"is_active": true,
"rate_limit_rpm": 120,
"raw_key": "maid_660e8400_xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
Important: The raw_key is only returned once. Store it securely.
Revoke API Key¶
DELETE /api/v1/admin/api-keys/{key_id}
Revoke an API key.
Required Permission: ADMIN
Response: 204 No Content
Admin User Management¶
Manage admin panel users. All endpoints require the ADMIN admin role. Only SUPERADMIN users can create, modify, or delete users with ADMIN or SUPERADMIN roles.
Admin Roles (hierarchical — each role includes permissions of lower roles):
| Role | Value | Description |
|---|---|---|
VIEWER |
10 | Read-only access to dashboards and stats |
MODERATOR |
20 | Can moderate players, kick, mute |
BUILDER |
30 | Can create/edit world content |
ADMIN |
40 | Full admin access, manage users |
SUPERADMIN |
50 | System-level access, configuration changes |
List Admin Users¶
GET /admin/users
List all admin users.
Required Role: ADMIN
Get Admin User¶
GET /admin/users/{user_id}
Get details for a specific admin user.
Required Role: ADMIN
Create Admin User¶
POST /admin/users
Create a new admin user.
Required Role: ADMIN (only SUPERADMIN can create ADMIN or SUPERADMIN users)
Request Body:
Update Admin User¶
PUT /admin/users/{user_id}
Update an admin user's role or status.
Required Role: ADMIN (only SUPERADMIN can modify ADMIN or SUPERADMIN users)
Note: Users cannot demote themselves.
Delete Admin User¶
DELETE /admin/users/{user_id}
Delete an admin user.
Required Role: ADMIN (only SUPERADMIN can delete ADMIN or SUPERADMIN users)
Note: Users cannot delete themselves.
WebSocket Events¶
WebSocket /api/v1/events
Stream real-time game events.
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
api_key |
str | Optional API key for authentication |
types |
str | Comma-separated event types to subscribe to |
Example Connection:
const ws = new WebSocket(
'ws://localhost:8080/api/v1/events?api_key=maid_xxx&types=player_login,chat_message'
);
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Event:', data.type, data.data);
};
Event Types:
| Type | Description |
|---|---|
player_login |
Player connected |
player_logout |
Player disconnected |
player_command |
Player executed a command |
chat_message |
Chat message sent |
system_message |
System message |
combat |
Combat started |
damage |
Damage dealt |
death |
Entity died |
respawn |
Entity respawned |
world_event |
World event occurred |
room_enter |
Entity entered a room |
room_leave |
Entity left a room |
entity_created |
Entity created |
entity_destroyed |
Entity destroyed |
component_added |
Component added to entity |
component_removed |
Component removed from entity |
tick |
Game tick (use sparingly) |
Event Message Format:
{
"type": "player_login",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"session_id": "xxx",
"player_id": "yyy"
}
}
Client Messages:
// Ping (server responds with pong)
{"type": "ping"}
// Update subscription
{"type": "subscribe", "types": ["combat", "chat_message"]}
WebSocket Protocol Extensions¶
The WebSocket protocol supports additional message types for richer client integration:
Request-Response Correlation:
Messages may include an optional requestId field. When present, the server echoes it back in the corresponding response, enabling clients to correlate requests with responses:
{"type": "subscribe", "types": ["combat"], "requestId": "abc-123"}
// Server response:
{"type": "subscribed", "types": ["combat"], "requestId": "abc-123"}
GMCP Data Subscription:
Use the subscribe message type to subscribe to GMCP data channels for structured game data:
Batched GMCP Updates:
The server may send a gmcp_batch message containing multiple GMCP updates in a single frame, reducing overhead for high-frequency data:
{
"type": "gmcp_batch",
"updates": [
{"package": "Char.Vitals", "data": {"hp": 95, "mp": 40}},
{"package": "Room.Info", "data": {"name": "Town Square"}}
]
}
Reconnect State Sync:
On reconnect, the server sends a sync_complete message containing a full state snapshot so the client can restore its view without requesting each piece individually:
{
"type": "sync_complete",
"data": {
"character": { "hp": 100, "mp": 50, "location": "Town Square" },
"room": { "name": "Town Square", "exits": ["north", "east"] },
"active_effects": []
}
}
Error Responses¶
All errors follow a consistent format:
HTTP Status Codes:
| Code | Description |
|---|---|
400 |
Bad Request - Invalid input |
401 |
Unauthorized - API key required |
403 |
Forbidden - Insufficient permissions |
404 |
Not Found - Resource does not exist |
409 |
Conflict - Resource already exists |
429 |
Too Many Requests - Rate limit exceeded |
500 |
Internal Server Error |
503 |
Service Unavailable - Engine not ready |
Example: curl Commands¶
Health Check¶
List Online Players¶
Get Player Details¶
curl "http://localhost:8080/api/v1/players/550e8400-e29b-41d4-a716-446655440000" \
-H "X-API-Key: maid_xxx_yyy"
Ban a Player¶
curl -X POST "http://localhost:8080/api/v1/players/550e8400.../ban" \
-H "X-API-Key: maid_xxx_yyy" \
-H "Content-Type: application/json" \
-d '{"reason": "Cheating", "duration_hours": 24}'
Broadcast Message¶
curl -X POST "http://localhost:8080/api/v1/admin/broadcast" \
-H "X-API-Key: maid_xxx_yyy" \
-H "Content-Type: application/json" \
-d '{"message": "Server restart in 5 minutes"}'
Create API Key¶
curl -X POST "http://localhost:8080/api/v1/admin/api-keys" \
-H "X-API-Key: maid_xxx_yyy" \
-H "Content-Type: application/json" \
-d '{"name": "Dashboard", "permissions": ["READ_PUBLIC", "READ_PLAYERS"]}'
Related Documentation¶
- CLI Reference - API key CLI commands
- SSL Configuration - HTTPS setup
- Bridges Guide - External service integration
- Admin API Reference - Full admin API documentation