The HTTP API provides REST endpoints for managing devices, controllers, jobs, and system configuration.
http://localhost:7072/api
Rotom supports optional header-based authentication for the API.
Authentication is configured in the TOML configuration file using the secret parameter:
# HTTP API authentication (optional)
[http_listener]
address = ":7072"
secret = "your-api-secret-here" # Optional authentication secretThe HTTP API supports header-based authentication when configured. When a secret is configured in http_listener.secret, all API endpoints will require authentication.
Required Headers (when authentication is enabled):
X-Rotom-Secret: your-secret-here
Authentication Behavior:
- If no
secretis configured: No authentication required - If
secretis configured: Authentication header is required for all API requests - Missing or invalid header returns
401 Unauthorized
Authentication Responses:
401 Unauthorized: Missing or invalid authentication header when authentication is required
GET /api/configHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns the current system configuration including version, tuning parameters, and rate limits.
Response:
{
"status": "ok",
"config": {
"version": "1.0.0",
"sha": "abc1234",
"instance": "optional-instance-name",
"tuning": {
"profiling": false
},
"rate_limit": {
"enable": true,
"max_selections": 100,
"duration": "1m"
}
}
}Notes:
sha: Git commit SHA of the running buildinstance: Only present if configuredrate_limit: Always present
PUT /api/config/reloadHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReloads the configuration from the config file and returns the updated configuration. Note: This does not reload jobs. Use the dedicated job reload endpoint to reload jobs.
Response: Same as GET /api/config
GET /api/statusHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns comprehensive status information including all devices and controllers.
Response:
{
"devices": [
{
"id": "device-123",
"origin": "192.168.1.100",
"version": "1.0",
"public_ip": "203.0.113.1",
"worker_count": 2,
"worker_in_use_count": 1,
"worker_in_use_percent": 50.0,
"worker_in_use_weight": 5,
"worker_max_weight": 10,
"worker_in_use_weight_percent": 50.0,
"last_connected_at_ms": 1694123456789,
"last_seen_at_ms": 1694123456789,
"enabled": true,
"is_connected": true,
"can_be_used": true,
"is_in_use": true,
"last_memory": {
"free": 1024000,
"mitm": 512000,
"start": 256000
},
"session": {
"connected_at_ms": 1694123456789,
"message_last_received_at_ms": 1694123456789,
"messages_received": 100,
"bytes_received": 50000,
"message_last_sent_at_ms": 1694123456789,
"messages_sent": 95,
"bytes_sent": 48000
},
"workers": [
{
"id": "worker-456",
"device_id": "device-123",
"origin": "192.168.1.100",
"version_code": 12345,
"version_name": "1.2.3",
"user_agent": "PokemodApp/1.2.3",
"last_connected_at_ms": 1694123456789,
"last_seen_at_ms": 1694123456789,
"message_last_received_at_ms": 1694123456789,
"messages_received": 100,
"bytes_received": 50000,
"message_last_sent_at_ms": 1694123456789,
"messages_sent": 95,
"bytes_sent": 48000,
"is_connected": true,
"is_in_use": true,
"platform": "android",
"weight": 5,
"can_be_used": true,
"session": {
"connected_at_ms": 1694123456789,
"message_last_received_at_ms": 1694123456789,
"messages_received": 80,
"bytes_received": 40000,
"message_last_sent_at_ms": 1694123456789,
"messages_sent": 80,
"bytes_sent": 40000,
"controller": {
"id": "controller-789",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"user_agent": "Controller/1.0",
"weight": 5,
"proto_major_version": 1,
"proto_minor_version": 0,
"account_username": "player123",
"account_source": "ptc",
"connected_at_ms": 1694123456789,
"message_last_received_at_ms": 1694123456789,
"messages_received": 50,
"bytes_received": 25000,
"message_last_sent_at_ms": 1694123456789,
"messages_sent": 48,
"bytes_sent": 24000
}
},
"time_windowed_stats": {
"requests_rate_over_30_seconds": 2.5,
"requests_rate_over_1_min": 2.3,
"requests_rate_over_5_min": 2.1,
"requests_rate_over_15_min": 2.0,
"request_ms_avg_over_30_seconds": 150.5,
"request_ms_avg_over_1_min": 155.2,
"request_ms_avg_over_5_min": 160.1,
"request_ms_avg_over_15_min": 165.0
}
}
]
}
],
"controllers": [
{
"id": "controller-789",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"user_agent": "Controller/1.0",
"weight": 5,
"proto_major_version": 1,
"proto_minor_version": 0,
"account_username": "player123",
"account_source": "ptc",
"connected_at_ms": 1694123456789,
"message_last_received_at_ms": 1694123456789,
"messages_received": 50,
"bytes_received": 25000,
"message_last_sent_at_ms": 1694123456789,
"messages_sent": 48,
"bytes_sent": 24000
}
]
}GET /api/controllerHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns a list of all connected controllers.
Response:
{
"controllers": [
{
"id": "controller-789",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"user_agent": "Controller/1.0",
"weight": 5,
"proto_major_version": 1,
"proto_minor_version": 0,
"worker_id": "optional-worker-id",
"account_username": "player123",
"account_source": "ptc",
"connected_at_ms": 1694123456789,
"message_last_received_at_ms": 1694123456789,
"messages_received": 50,
"bytes_received": 25000,
"message_last_sent_at_ms": 1694123456789,
"messages_sent": 48,
"bytes_sent": 24000
}
]
}GET /api/controller/{uuid}Headers (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns details for a specific controller.
Path Parameters:
uuid(string): Controller UUID
Response:
{
"controller": {
"id": "controller-789",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"user_agent": "Controller/1.0",
"weight": 5,
"proto_major_version": 1,
"proto_minor_version": 0,
"worker_id": "optional-worker-id",
"account_username": "player123",
"account_source": "ptc",
"connected_at_ms": 1694123456789,
"message_last_received_at_ms": 1694123456789,
"messages_received": 50,
"bytes_received": 25000,
"message_last_sent_at_ms": 1694123456789,
"messages_sent": 48,
"bytes_sent": 24000
}
}Error Responses:
404: Controller not found
PUT /api/controller/{uuid}/action/{action}Headers (when authentication is enabled):
X-Rotom-Secret: your-secret-herePerforms an action on a specific controller.
Path Parameters:
uuid(string): Controller UUIDaction(string): Action to perform
Available Actions:
disconnect: Disconnect controller (single controller only)reconnect: Tell controller to reconnect (single controller only)
Response for disconnect:
{
"status": "ok",
"message": "Controller disconnected."
}Response for reconnect:
{
"status": "ok",
"message": "Controller set to be told to reconnect/restart session."
}Error Responses:
404: Controller not found or action not found500: Internal server error
GET /api/deviceHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns a list of all devices.
Query Parameters:
include_workers(boolean): Include worker details in response (default: false)
Response:
{
"devices": [
{
"id": "device-123",
"origin": "192.168.1.100",
"version": "1.0",
"public_ip": "203.0.113.1",
"worker_count": 2,
"worker_in_use_count": 1,
"worker_in_use_percent": 50.0,
"worker_in_use_weight": 5,
"worker_max_weight": 10,
"worker_in_use_weight_percent": 50.0,
"last_connected_at_ms": 1694123456789,
"last_seen_at_ms": 1694123456789,
"enabled": true,
"is_connected": true,
"can_be_used": true,
"is_in_use": true,
"last_memory": {
"free": 1024000,
"mitm": 512000,
"start": 256000
},
"session": {
"connected_at_ms": 1694123456789,
"message_last_received_at_ms": 1694123456789,
"messages_received": 100,
"bytes_received": 50000,
"message_last_sent_at_ms": 1694123456789,
"messages_sent": 95,
"bytes_sent": 48000
},
"workers": []
}
]
}GET /api/device/{deviceId}Headers (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns details for a specific device.
Path Parameters:
deviceId(string): Device identifier
Query Parameters:
include_workers(boolean): Include worker details in response (default: false)
Response: Same structure as single device in GET /api/device
Error Responses:
404: Device not found
PUT /api/device/{deviceId}/action/{action}Headers (when authentication is enabled):
X-Rotom-Secret: your-secret-herePerforms an action on a specific device.
Path Parameters:
deviceId(string): Device identifier or "_" for bulk operations (limited actions)action(string): Action to perform
Available Actions:
delete: Remove device (supports "_" for removing all unconnected devices)restart: Restart device app (single device only)reboot: Reboot device (single device only)logcat: Download device logs as ZIP file (single device only)disable: Disable device for selection (single device only)enable: Enable device for selection (single device only)disconnect: Disconnect device and all its workers (single device only)
Response for most actions:
{
"status": "ok",
"message": "Action completed successfully",
"device": {
// Updated device object (for enable/disable actions)
}
}Response for delete with "_":
{
"status": "ok",
"message": "Removed dead connections",
"devices_count": 2
}Response for logcat: Binary ZIP file download with headers:
Content-Disposition: attachment; filename="logcat-{deviceId}-{timestamp}.zip"Content-Type: application/zip
Response for disconnect:
{
"status": "ok",
"message": "Device disconnected."
}Important Notes:
- Device disconnect behavior: When a device is disconnected using the
disconnectaction, all workers associated with that device are automatically disconnected as well. This ensures that no orphaned worker connections remain when a device goes offline.
Error Responses:
400: Invalid action or device state404: Device not found500: Internal server error
GET /api/jobHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns a list of all available jobs.
Response:
{
"jobs": [
{
"id": "job-1",
"description": "Example job description",
"exec": "command to execute"
}
]
}Error Responses:
404: Jobs are not enabled
GET /api/job/{jobId}Headers (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns details for a specific job.
Path Parameters:
jobId(string): Job identifier
Response:
{
"job": {
"id": "job-1",
"description": "Example job description",
"exec": "command to execute"
}
}Error Responses:
404: Job not found or jobs not enabled
PUT /api/job/-/reloadHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReloads all jobs from the configured jobs directory without requiring a full configuration reload.
Response:
{
"status": "ok",
"message": "Jobs reloaded successfully"
}Error Responses:
404: Jobs are not enabled500: Failed to reload jobs (error details in response)
PUT /api/job/{jobId}/runHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereExecutes a job on specified devices.
Path Parameters:
jobId(string): Job identifier
Request Body:
{
"device_ids": ["device-1", "device-2"]
}Response:
{
"instances": [
{
"id": 12345,
"job_id": "job-1",
"started_at_ms": 1694123456789,
"finished_at_ms": 0,
"device_id": "device-1",
"device_origin": "192.168.1.100",
"result": "",
"status": "running"
}
]
}Error Responses:
400: Invalid request body or no device IDs provided404: Job not found or jobs not enabled
GET /api/job-instanceHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns a list of all job instances (sorted by ID, newest first).
Response:
{
"instances": [
{
"id": 12345,
"job_id": "job-1",
"started_at_ms": 1694123456789,
"finished_at_ms": 1694123456890,
"device_id": "device-1",
"device_origin": "192.168.1.100",
"result": "Job completed successfully",
"status": "completed"
}
]
}Error Responses:
404: Jobs are not enabled
GET /api/job-instance/{jobInstanceId}Headers (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns details for a specific job instance.
Path Parameters:
jobInstanceId(uint64): Job instance identifier
Response:
{
"job": {
"id": 12345,
"job_id": "job-1",
"started_at_ms": 1694123456789,
"finished_at_ms": 1694123456890,
"device_id": "device-1",
"device_origin": "192.168.1.100",
"result": "Job completed successfully",
"status": "completed"
}
}Error Responses:
404: Job instance not found or jobs not enabled
PUT /api/job-instance/{jobInstanceId}/clearHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereClears a specific job instance or all instances.
Path Parameters:
jobInstanceId(string): Job instance identifier or "-" to clear all instances
Response:
{
"status": "success"
}Error Responses:
404: Job instance not found or jobs not enabled
GET /api/metricsHeaders (when authentication is enabled):
X-Rotom-Secret: your-secret-hereReturns Prometheus metrics (only available if Prometheus is enabled in configuration).
Response: Prometheus metrics format
All debug endpoints require profiling to be enabled in the configuration.
Headers (when authentication is enabled):
X-Rotom-Secret: your-secret-hereGET /api/debug/pprof/Returns pprof profiling index page.
GET /api/debug/pprof/cmdlineReturns command line arguments.
GET /api/debug/pprof/profileReturns CPU profile data.
GET /api/debug/pprof/symbolReturns symbol table information.
GET /api/debug/pprof/traceReturns execution trace data.
Error Response for all debug endpoints when profiling is disabled:
{
"error": "profiling disabled"
}All connection objects include these common statistics:
{
"message_last_received_at_ms": 1694123456789,
"messages_received": 100,
"bytes_received": 50000,
"message_last_sent_at_ms": 1694123456789,
"messages_sent": 95,
"bytes_sent": 48000
}{
"free": 1024000,
"mitm": 512000,
"start": 256000
}Available for workers in requests mode or proxy mode with inspect proxy mode:
{
"requests_rate_over_30_seconds": 2.5,
"requests_rate_over_1_min": 2.3,
"requests_rate_over_5_min": 2.1,
"requests_rate_over_15_min": 2.0,
"request_ms_avg_over_30_seconds": 150.5,
"request_ms_avg_over_1_min": 155.2,
"request_ms_avg_over_5_min": 160.1,
"request_ms_avg_over_15_min": 165.0
}Fields:
requests_rate_over_*: Request rate per second over the specified time windowrequest_ms_avg_over_*: Average request duration in milliseconds over the specified time window
Additional worker-specific fields:
platform(string): Device platform (e.g., "android", "ios")weight(int): Weight of the assigned controller — only present when worker is in usetime_windowed_stats(object): Time-windowed statistics (only present for certain worker modes)
pending: Job is queued but not startedrunning: Job is currently executingcompleted: Job finished successfullyfailed: Job failed with errorcancelled: Job was cancelled
All API endpoints return consistent error responses:
{
"status": "error",
"error": "Error description"
}Common HTTP status codes:
200: Success400: Bad Request (invalid parameters, device state issues)404: Not Found (resource doesn't exist, feature disabled)500: Internal Server Error
Rate limiting can be configured per device and applies to worker selection. When enabled, it limits the number of worker selections per time duration.
The system supports runtime configuration reloading via the /api/config/reload endpoint. Configuration changes take effect immediately without requiring a restart.