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:
Server Status¶
GET /api/v1/status
Public server status endpoint. No authentication required. Returns operational context including maintenance state, player count, and version info. Intended for status pages, monitors, and the player frontend's pre-login screen.
Response Headers:
Cache-Control: public, max-age=30Access-Control-Allow-Origin: *
Response (200):
{
"status": "ok",
"state": "ready",
"instance": "prod",
"version": "0.1.0",
"uptime_seconds": 86400.5,
"tick_rate_hz": 4.0,
"players_online": 12,
"last_save": "2024-01-15T10:30:00Z"
}
The maintenance key is omitted entirely when no maintenance window is active. When a maintenance window is active, the maintenance sub-object is included:
{
"status": "ok",
"state": "maintenance",
"instance": "prod",
"version": "0.1.0",
"uptime_seconds": 86400.5,
"tick_rate_hz": 4.0,
"players_online": 0,
"last_save": "2024-01-15T10:29:00Z",
"maintenance": {
"active": true,
"reason": "Scheduled maintenance window",
"eta": "2024-01-15T11:00:00Z",
"status_url": "https://status.example.com"
}
}
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"
}
Get CSRF Token¶
GET /api/v1/auth/csrf
Issue a CSRF token for double-submit cookie protection. Sets an XSRF-TOKEN cookie and returns the token in the response body. No authentication required.
Response (200):
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
Aggregate health across registered probes. Returns 200 when healthy, 503 when a critical probe is failing.
Response (200):
{
"status": "ok",
"summary": "all probes healthy",
"probes": {
"database": {"ok": true, "kind": "critical", "detail": "connected"}
}
}
statusis one ofok,warn, orfail. When no probes are registered the server returns a legacy shape instead:{"status": "healthy", "checks": {…}}.
Readiness Check¶
GET http://localhost:9090/readyz
Readiness check. Returns 200 when the instance can accept player traffic, and 503 while draining or when a readiness check fails.
Response (200):
statusis one ofhealthy,degraded, orunhealthy.
Liveness Check¶
GET http://localhost:9090/livez
Liveness check. Returns 200 while the tick loop is advancing, and 503 if it has stalled.
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. Authentication is performed via a JSON auth message (not query parameters). Connections that do not send a valid auth frame are closed with code 4001.
Authentication:
After establishing the WebSocket connection, the client must send a JSON authentication message as the first frame:
| Field | Type | Description |
|---|---|---|
type |
str | Must be "auth" |
api_key |
str | A valid API key |
types |
list[str] | Optional. Event types to subscribe to. If omitted, subscribes to all events permitted by the API key's permissions. |
Success Response:
On successful authentication the server sends an authenticated message containing the assigned connection ID, the resolved subscription list, and a human-readable message:
{
"type": "authenticated",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"connection_id": 1,
"subscribed_types": ["player_login", "chat_message"],
"message": "Authenticated. Connected to MAID events stream."
}
}
If no types filter was provided in the auth frame, subscribed_types is an array of all event types permitted by the API key's permissions.
Auth Failure Response:
On failure the server sends a type: "error" frame with data.code set to "AUTH_FAILED" and then closes the connection with WebSocket close code 4001:
{
"type": "error",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"code": "AUTH_FAILED",
"message": "Invalid API key"
}
}
Rate-limited connections receive close code 4029 instead.
Example Connection:
const ws = new WebSocket('ws://localhost:8080/api/v1/events');
ws.onopen = () => {
// Send auth frame as first message
ws.send(JSON.stringify({
type: 'auth',
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"]}
Subscribe Response:
When the server processes a subscribe message it responds with a subscribed acknowledgement containing the resolved event types (filtered by the key's permissions):
{
"type": "subscribed",
"timestamp": "2024-01-15T10:31:00Z",
"data": {
"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