HTTP API
Complete reference for the Ctrl Plane REST API endpoints.
All endpoints are prefixed with /v1. Requests and responses use JSON. Authentication is handled through bearer tokens in the Authorization header.
Authentication
Every request must include a bearer token:
Authorization: Bearer <token>The auth middleware extracts tenant and user identity from the token. The specific token format depends on which auth.Provider is configured.
Instances
Create instance
POST /v1/instances
Content-Type: application/json
{
"tenant_id": "ten_...",
"name": "api-server",
"image": "myapp:v1.0",
"provider_name": "docker",
"resources": { "cpu_millis": 500, "memory_mb": 256 },
"ports": [{ "container_port": 8080, "protocol": "tcp" }],
"env": { "NODE_ENV": "production" },
"labels": { "team": "platform" }
}Response: 201 Created with the full instance object.
List instances
GET /v1/instances?cursor=...&limit=20Response: 200 OK with { "items": [...], "next_cursor": "...", "total": 42 }.
Get instance
GET /v1/instances/{instanceID}Update instance
PATCH /v1/instances/{instanceID}
Content-Type: application/json
{
"name": "new-name",
"labels": { "team": "backend" }
}Partial updates: only fields present in the request body are modified.
Delete instance
DELETE /v1/instances/{instanceID}Lifecycle actions
POST /v1/instances/{instanceID}/start
POST /v1/instances/{instanceID}/stop
POST /v1/instances/{instanceID}/restartScale
POST /v1/instances/{instanceID}/scale
Content-Type: application/json
{
"resources": { "cpu_millis": 2000, "memory_mb": 1024 }
}Suspend / Unsuspend
POST /v1/instances/{instanceID}/suspend
Content-Type: application/json
{ "reason": "payment overdue" }POST /v1/instances/{instanceID}/unsuspend
Content-Type: application/json
{ "reason": "payment received" }Deployments
Deploy
POST /v1/instances/{instanceID}/deploy
Content-Type: application/json
{
"image": "myapp:v2.0",
"strategy": "rolling",
"env": { "FEATURE_X": "true" },
"notes": "enable feature X",
"commit_sha": "abc123"
}List deployments
GET /v1/instances/{instanceID}/deployments?cursor=...&limit=20Get deployment
GET /v1/deployments/{deploymentID}Cancel deployment
POST /v1/deployments/{deploymentID}/cancelRollback
POST /v1/instances/{instanceID}/rollback
Content-Type: application/json
{ "release_id": "rel_..." }List releases
GET /v1/instances/{instanceID}/releases?cursor=...&limit=20Get release
GET /v1/releases/{releaseID}Health
Configure health check
POST /v1/instances/{instanceID}/health/checks
Content-Type: application/json
{
"name": "api-health",
"type": "http",
"target": "http://localhost:8080/healthz",
"interval": "30s",
"timeout": "5s",
"retries": 3
}Get instance health
GET /v1/instances/{instanceID}/healthReturns aggregate health across all checks.
List health checks
GET /v1/instances/{instanceID}/health/checksRun check manually
POST /v1/health/checks/{checkID}/runDelete check
DELETE /v1/health/checks/{checkID}Telemetry
Get metrics
GET /v1/instances/{instanceID}/metrics?name=http_requests_total&since=2024-01-01T00:00:00ZGet logs
GET /v1/instances/{instanceID}/logs?level=error&limit=50Get traces
GET /v1/instances/{instanceID}/traces?limit=20Get resources
GET /v1/instances/{instanceID}/resourcesGet dashboard
GET /v1/instances/{instanceID}/dashboardNetwork
Add domain
POST /v1/instances/{instanceID}/domains
Content-Type: application/json
{ "hostname": "app.example.com" }List domains
GET /v1/instances/{instanceID}/domainsVerify domain
POST /v1/domains/{domainID}/verifyProvision certificate
POST /v1/domains/{domainID}/certDelete domain
DELETE /v1/domains/{domainID}Add route
POST /v1/instances/{instanceID}/routes
Content-Type: application/json
{
"path": "/api",
"port": 8080,
"protocol": "http",
"weight": 100,
"strip_prefix": true
}Update route
PATCH /v1/routes/{routeID}Delete route
DELETE /v1/routes/{routeID}Secrets
Set secret
POST /v1/instances/{instanceID}/secrets
Content-Type: application/json
{
"key": "DATABASE_URL",
"value": "postgres://...",
"type": "env"
}List secrets
GET /v1/instances/{instanceID}/secretsValues are never returned in list responses.
Delete secret
DELETE /v1/instances/{instanceID}/secrets/{key}Admin
System stats
GET /v1/admin/statsList providers
GET /v1/admin/providersCreate tenant
POST /v1/admin/tenants
Content-Type: application/json
{
"name": "Acme Corp",
"slug": "acme",
"plan": "pro"
}List tenants
GET /v1/admin/tenantsGet / Update / Delete tenant
GET /v1/admin/tenants/{tenantID}
PATCH /v1/admin/tenants/{tenantID}
DELETE /v1/admin/tenants/{tenantID}Suspend / Unsuspend tenant
POST /v1/admin/tenants/{tenantID}/suspend
POST /v1/admin/tenants/{tenantID}/unsuspendGet / Set quota
GET /v1/admin/tenants/{tenantID}/quota
PUT /v1/admin/tenants/{tenantID}/quotaQuery audit log
GET /v1/admin/audit?tenant_id=...&resource=instance&action=create&limit=50Error responses
All errors return a JSON object with a message field:
{
"message": "instance inst_01h455vb...: ctrlplane: resource not found"
}See Error Handling for the status code mapping.