Automation API
The Automation API (/api/automation/v1/) lets external systems run operations on your servers. Call it from GitHub Actions, GitLab CI, Jenkins, or any system that can make HTTP requests.
For a setup guide with GitHub Actions, see GitHub Actions Integration.
Authentication
Section titled “Authentication”Every request carries an automation key in the Authorization header:
Authorization: Bearer rca_your_key_hereCreate keys in the dashboard. Open API Keys, select the Automation Keys tab, then click Create Key. Each key holds a list of operations and a scope of servers, registry credentials, and secret projects.
For the difference between API keys and automation keys, see API Overview. For the secret and target identifier each integration method uses, see the Credentials reference.
What an automation key can call
Section titled “What an automation key can call”Every endpoint outside the catalog returns 403:
{ "detail": "Automation keys cannot use this endpoint."}A key that lacks the operation for a covered route gets a 403 that names the operation to add:
{ "detail": "This automation key does not have the `server:exec` operation (Run shell commands). Add the operation to the key, then try again."}Two routes stay open to every valid key, whatever its operations:
| Route | Why |
|---|---|
GET /api/automation/v1/operations/{operation_id} | Poll a job this key started. /exec and /deploy return a job id to poll. |
GET /tenants/{tenant_id}/completion-index | Shell completion data for the CLI. It returns the servers slice only to an automation key. |
Routes each operation covers
Section titled “Routes each operation covers”| Operation | Routes |
|---|---|
application:deploy | POST /api/automation/v1/deploy |
deploy_session:open | POST /external-deploy/session, DELETE /external-deploy/session/{session_id}, POST /external-deploy/sync, GET /external-deploy/status |
container:restart | POST /api/automation/v1/restart |
server:exec | POST /api/automation/v1/exec |
server:terminal | WEBSOCKET /api/automation/v1/ws/terminal/{server_id}, WEBSOCKET /mcp/ws/terminal/{server_id}, WEBSOCKET /ws/terminal/{server_id} |
server:reboot | POST /api/automation/v1/reboot |
secret_session:open | POST /api/automation/v1/secrets/open-session, POST /api/automation/v1/secrets/resolve, GET /api/automation/v1/secrets/accessible-projects |
registry:login | POST /api/automation/v1/registry-auth/login, POST /api/automation/v1/registry-auth/login-direct |
registry:logout | POST /api/automation/v1/registry-auth/logout |
Operation ids read as resource:verb. Keys created before this change hold flat names such as deploy and exec. Reoclo accepts those names as aliases, so existing keys keep working.
| Old name | Canonical operation |
|---|---|
deploy | application:deploy |
restart | container:restart |
exec | server:exec |
reboot | server:reboot |
terminal | server:terminal |
registry_login | registry:login |
registry_logout | registry:logout |
external_deploy | deploy_session:open |
Endpoints
Section titled “Endpoints”Execute Command
Section titled “Execute Command”POST /api/automation/v1/execOperation: server:exec.
Run a shell command on a server through the runner.
Request:
{ "server_id": "uuid", "command": "docker compose up -d", "working_directory": "/srv/reoclo/myapp", "env": { "DB_URL": "postgres://...", "API_KEY": "secret" }, "timeout_seconds": 300, "run_id": "github-run-12345", "run_context": { "provider": "github_actions", "repository": "myorg/myapp", "workflow": "deploy", "trigger": "push", "actor": "username", "sha": "abc123", "ref": "refs/heads/main" }}| Field | Required | Default | Description |
|---|---|---|---|
server_id | Yes | - | Target server ID |
command | Yes | - | Shell command (max 8,192 chars) |
working_directory | No | - | Working directory on the server |
env | No | - | Environment variables (max 200 keys, 64KB total) |
timeout_seconds | No | 60 | Max execution time (1-900 seconds) |
run_id | No | - | Groups related operations, such as a GitHub run ID |
run_context | No | - | CI metadata for audit trail |
Response (completed):
{ "operation_id": "uuid", "status": "completed", "exit_code": 0, "stdout": "...", "stderr": "", "duration_ms": 4500}Response (still running):
{ "operation_id": "uuid", "status": "running"}If the command finishes within timeout_seconds, the response carries the full result. Otherwise, poll the operation status endpoint.
Trigger Deployment
Section titled “Trigger Deployment”POST /api/automation/v1/deployOperation: application:deploy.
Trigger a deployment for an application.
Request:
{ "application_id": "uuid", "commit_ref": "main", "env_overrides": { "FEATURE_FLAG": "true" }, "run_id": "github-run-12345", "run_context": { ... }}| Field | Required | Default | Description |
|---|---|---|---|
application_id | Yes | - | Application to deploy |
commit_ref | No | App’s deploy branch | Branch, tag, or SHA |
env_overrides | No | - | Ephemeral env vars for this deployment only (not saved) |
timeout_seconds | No | 60 | Max wait time |
run_id | No | - | Groups related operations |
run_context | No | - | CI metadata |
Response:
{ "operation_id": "uuid", "status": "running", "deployment_id": "uuid"}Deployments are always async. Poll the operation status for completion.
Restart Container
Section titled “Restart Container”POST /api/automation/v1/restartOperation: container:restart.
Restart the containers of an application.
Request:
{ "server_id": "uuid", "container_name": "myapp", "run_id": "github-run-12345"}Reboot Server
Section titled “Reboot Server”POST /api/automation/v1/rebootOperation: server:reboot.
Reboot a server. Reoclo uses the cloud provider API when one is available, and falls back to the runner.
Request:
{ "server_id": "uuid", "run_id": "github-run-12345"}Registry Login
Section titled “Registry Login”POST /api/automation/v1/registry-auth/loginOperation: registry:login.
Sign in to a container registry on the target server, using a registry credential stored in your Reoclo tenant. This endpoint is asynchronous. It creates an operation record and returns 202 immediately. Poll the operation status endpoint to track progress.
The API never returns the plaintext registry password. The response body carries the resolved registry URL and the registry provider type only.
Request:
{ "server_id": "uuid", "credential_id": "uuid", "run_id": "github-run-12345", "run_context": { "provider": "github_actions", "repository": "myorg/myapp", "...": "..." }}| Field | Required | Description |
|---|---|---|
server_id | Yes | Target server ID (the runner must be connected) |
credential_id | Yes | Registry credential UUID. Must be in the key’s allowed_credential_ids if scoped. |
run_id | No | Groups related operations |
run_context | No | CI metadata for audit trail |
Response (202 Accepted):
{ "operation_id": "uuid", "status": "running", "registry_url": "ghcr.io", "registry_type": "ghcr"}Poll /operations/{operation_id} for the final result. The completed operation carries the exit_code, the captured stdout, the captured stderr, and duration_ms. Reoclo records the login and any later logout in the audit log.
Registry Logout
Section titled “Registry Logout”POST /api/automation/v1/registry-auth/logoutOperation: registry:logout.
Run docker logout on the target server. This is the paired cleanup for registry-auth/login, which a GitHub Action post-step usually calls. Synchronous.
Request:
{ "server_id": "uuid", "registry_url": "ghcr.io", "run_id": "github-run-12345"}Response:
{ "operation_id": "uuid", "status": "completed"}A logout failure does not return HTTP 5xx. It returns a completed operation with a non-zero exit code. Log those as warnings rather than errors.
Secret Session
Section titled “Secret Session”POST /api/automation/v1/secrets/open-sessionPOST /api/automation/v1/secrets/resolveGET /api/automation/v1/secrets/accessible-projectsOperation: secret_session:open.
These endpoints open a short-lived, read-only secret session for one CI run. The session reads only the secret projects granted to the key. An automation key can never create, change, roll back, or delete a secret.
Most pipelines call reoclo run instead of these endpoints. See Secrets in CI.
Poll Operation Status
Section titled “Poll Operation Status”GET /api/automation/v1/operations/{operation_id}Check the status of a running operation. Every valid key can call this route, whatever its operations.
Response:
{ "operation_id": "uuid", "operation_type": "exec", "server_id": "uuid", "server_name": "prod-1", "status": "completed", "result": { "exit_code": 0, "stdout": "...", "stderr": "", "duration_ms": 4500 }, "run_id": "github-run-12345", "started_at": "2026-04-10T14:30:00Z", "completed_at": "2026-04-10T14:30:04Z"}Status values: running, completed, failed, timeout
Operation Catalog
Section titled “Operation Catalog”GET /tenants/{tenant_id}/automation-keys/catalogList every operation a key can hold, plus the presets. The dashboard renders the create and edit forms from this response.
This route sits outside /api/automation/v1/. Call it with a user session, not with an automation key. The caller needs the manager role or higher.
Response:
{ "operations": [ { "id": "server:exec", "label": "Run shell commands", "description": "Run a shell command on a server.", "group": "servers", "group_label": "Servers", "risk": "critical", "server_scoped": true, "note": "A key with this operation can do anything the runner user can do.", "legacy_aliases": ["exec"] } ], "presets": [ { "id": "github_actions_deploy", "label": "Deploy from GitHub Actions", "description": "Reoclo builds and runs the containers. The usual choice.", "operations": [ "application:deploy", "container:restart", "registry:login", "registry:logout" ] } ]}Each entry in operations carries:
| Field | Description |
|---|---|
id | Canonical resource:verb operation id. Store this one on the key. |
label | Short name the dashboard shows. |
description | One sentence on what the operation does. |
group | Group id: deployments, containers, servers, secrets, or registry. |
group_label | Display name of the group. |
risk | standard, elevated, or critical. |
server_scoped | Whether the key’s server allowlist narrows this operation. |
note | Extra detail the dashboard shows under the description. Can be null. |
legacy_aliases | Old flat names that still resolve to this operation. |
Each entry in presets carries an id, a label, a description, and the operations it selects.
API Key Scoping
Section titled “API Key Scoping”Each automation key carries these limits:
| Setting | Description |
|---|---|
| Operations | Which routes the key can call. An empty list allows nothing. |
| Servers | Which servers the key can target. Empty = all servers. |
| Registry credentials | Which registry credentials the key can pass to registry-auth/login. Empty = all credentials in the tenant. Reoclo rejects cross-tenant references at create and update time. |
| Secret projects | Which secret projects a secret session can read. Grant each project on its Access tab. |
| IP allowlist | Restrict to specific IPs or CIDR ranges, such as GitHub runner IPs. Empty = allow all. |
| Rate limit | Requests each minute (default: 100, max: 1000). |
| Expiration | Optional expiry date for the key. |
Editing the expiry date of an existing key now works. Earlier versions reported success and changed nothing.
Rate Limiting
Section titled “Rate Limiting”Each key carries a rate limit, 100 requests each minute by default. When a key exceeds that limit:
- The API returns HTTP
429 Too Many Requests. - The
Retry-Afterheader gives the time to retry. - Reoclo logs the rate limit event in the audit trail.
Environment Variables
Section titled “Environment Variables”Environment variables that you send in env (for exec) or env_overrides (for deploy) behave as follows:
- The runner injects them into the command environment on the server.
- Reoclo never stores them in its database.
- Audit logs hold the key names only. Reoclo redacts the values.
- The payload holds at most 200 keys and 64KB.
Audit Trail
Section titled “Audit Trail”Reoclo logs every operation with:
- The command that ran
- The server it ran on
- Exit code, stdout, stderr
- The API key that made the call
- CI context: repository, workflow, actor, commit SHA
Reoclo groups operations from the same CI run by run_id. Click the operation count on any automation key row to read its operation history in Logs.
Error Responses
Section titled “Error Responses”| Code | Meaning |
|---|---|
401 | Invalid, revoked, or expired API key |
403 | Endpoint not covered by any operation, operation not on the key, server not in scope, or IP not in the allowlist |
404 | Server or application not found |
422 | Validation error, such as an env payload too large or an invalid timeout |
429 | Rate limit exceeded |
All errors return:
{ "detail": "Human-readable error message"}Next Steps
Section titled “Next Steps”- GitHub Actions Integration for step-by-step setup with
@reoclo/run - API Keys for the full operation list and presets
- API Overview for general API authentication
- Scheduled Operations for server-side automation without CI