Workspace API
Everything in a workspace that isn’t a job or a run: API keys, secrets, alerting, statistics and the audit log, plus the server’s health endpoints.
Identity and workspace
GET /api/v1/me returns the user and accessible workspaces. GET /api/v1/workspaces/{ws} returns id, name, slug, plan and created_at; PATCH accepts name and slug. The workspace must match the authenticated workspace; other IDs return 404.
API keys
| Endpoint | Description |
|---|---|
GET /api/v1/api-keys | List keys: id, name, prefix (e.g. cp_ab12cd34), scopes, created_at, last_used_at, revoked_at |
POST /api/v1/api-keys | Create: {"name": "ci", "scopes": ["jobs:read","runs:read"]}. Response includes the full key once. |
DELETE /api/v1/api-keys/{id} | Revoke. Takes effect immediately. |
Secrets
| Endpoint | Description |
|---|---|
GET /api/v1/secrets | List names with created_at/updated_at. Values are never returned. |
PUT /api/v1/secrets/{name} | Create or replace: {"value": "…"} |
DELETE /api/v1/secrets/{name} | Delete |
See Secrets.
Alert channels
| Endpoint | Description |
|---|---|
GET /api/v1/alert-channels | List |
POST /api/v1/alert-channels | Create. Email channels start unverified. Webhook channels return a signing_secret once. |
GET /api/v1/alert-channels/{id} | Read one channel |
GET /api/v1/alert-channels/verify?token=… | Verify an address using its signed email link (no login required) |
POST /api/v1/alert-channels/{id}/verify | Resend confirmation links |
PATCH /api/v1/alert-channels/{id} | Update |
DELETE /api/v1/alert-channels/{id} | Delete (rules referencing it stop sending to it) |
POST /api/v1/alert-channels/{id}/test | Send a test alert |
See Alert channels.
Alert rules
| Endpoint | Description |
|---|---|
GET /api/v1/alert-rules | List |
POST /api/v1/alert-rules | Create |
GET /api/v1/alert-rules/{id} | Read one rule |
PATCH /api/v1/alert-rules/{id} | Update |
DELETE /api/v1/alert-rules/{id} | Delete |
See Alert rules.
Stats
GET /api/v1/stats/overview?window=24h (1h, 24h, 7d, 30d):
{
"window": "24h",
"jobs": {"active": 42, "paused": 3, "disabled": 1, "completed": 7},
"runs": {"total": 18240, "succeeded": 18190, "failed": 50, "sla_breached": 4},
"start_lag_ms": {"p50": 3, "p95": 9, "p99": 41},
"failing_jobs": [{"id": "job_01J9…", "name": "crm-sync", "consecutive_failures": 6}]
}
Audit log
GET /api/v1/audit-log?action=job.pause&limit=50. Each entry has actor (user:…, key:… or system), action (e.g. job.create, job.update, job.pause, job.auto_disable, api_key.create, secret.update), target, details and created_at. Entries are kept for 30 days by default.
Health (no authentication, outside /api/v1)
| Endpoint | Returns |
|---|---|
GET /healthz | 200 while the process is running. Use for liveness probes. |
GET /readyz | 200 when both databases are writable, the queue file responds, the dispatcher is active and result writing is keeping up; 503 otherwise, with a JSON breakdown. Use for readiness probes and uptime checks. |
GET /version | {"version": "1.0.0", "commit": "…", "build_date": "…", "edition": "oss"} |
{"dispatcher": "active", "queue": true, "heartbeat": "2026-10-18T09:30:00Z", "writer_backpressure": false, "sqlite_writable": true}
dispatcher is one of starting, active, standby (another process holds the dispatcher lock) or stopped. queue is false when the queue file can’t be read, sqlite_writable covers both databases, and heartbeat is the dispatcher’s last tick.
Usage and suspension
GET /api/v1/usage returns the workspace’s UTC calendar-month run usage and effective limit:
{"month":"2026-10","runs":1200,"limit":50000,"over_quota_policy":"block","reset_at":"2026-11-01T00:00:00Z"}
limit is 0 for unlimited. over_quota_policy is block or allow. reset_at is the start of the next UTC month. At a blocking cap, manual job triggers return 429 with error.code: "limit_exceeded" and limit: "max_runs_per_month_per_workspace".
Suspended workspaces retain read access. Mutations return 403 with error.code: "workspace_suspended"; operators restore access with the cronpad workspace unsuspend <slug> command. Skipped runs carry a skip_reason (quota_exceeded, workspace_suspended, previous_run_in_progress or misfire) separate from delivery errors in last_error.