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. No authentication required.
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. No authentication required.
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. No authentication required.
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. No authentication required.
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
}
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
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"]}
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