Skip to content
cronpad
API reference / API reference

API reference

The cronpad REST API lives under /api/v1 on the same host as the dashboard. It speaks JSON, authenticates with a workspace API key in the Authorization header, returns errors in one consistent shape, and uses cursor pagination. The dashboard uses exactly this API, so anything you can do in the UI you can do with it. A machine-readable OpenAPI 3.1 spec is served at /api/v1/openapi.yaml.

SectionEndpoints
JobsCreate, list, get, update, delete, pause, resume, enable, trigger, test, rotate signing secret
RunsList runs, get a run with attempts, retry a failed run
SchedulesValidate and preview schedules (no auth required)
WorkspaceAPI keys, secrets, alert channels and rules, stats, audit log, health

Server features

GET /api/v1/features needs no authentication and returns what this server offers; the dashboard uses it to decide which sign-in buttons and sink types to show. It never includes secrets or client IDs, and clients may cache it for a minute.

{
  "public_url": "https://cronpad.example.com",
  "auth": {"providers": ["github", "google"], "dev_login": false},
  "sinks": {"webhook": true, "command": false}
}

Base URL

https://app.cronpad.com/api/v1          # cronpad Cloud
https://<your public_url>/api/v1         # self-hosted

Authentication

Send an API key as a bearer token:

curl https://app.cronpad.com/api/v1/jobs -H "Authorization: Bearer cp_ab12cd34_…"
  • Create keys under Settings → API keys or with POST /api/v1/api-keys. The full key is shown once; cronpad stores only a hash.
  • A key belongs to one workspace and can only see that workspace.
  • Scopes: * (default, full access), jobs:read, jobs:write, runs:read. A request outside the key’s scopes gets 403 forbidden.

The dashboard authenticates with a session cookie instead, and sends the active workspace in a Cronpad-Workspace header. Cookie-authenticated requests that change data also need the X-CSRF-Token header; API-key requests don’t.

Conventions

  • Times are RFC 3339 in UTC (2026-11-10T03:30:00Z).
  • Durations are Go-style strings: "30s", "10m", "1h30m".
  • IDs are prefixed ULIDs: job_…, run_…, ws_…, key_…, ch_…, rule_…. They sort by creation time.
  • Request bodies are limited to 1 MiB.
  • Every response has an X-Request-ID header (echoed if you send one). Include it when reporting problems.

Errors

{
  "error": {
    "code": "validation_failed",
    "message": "schedule: unknown word \"tuesdy\"",
    "fields": {"schedule.expr": "unknown word \"tuesdy\" at position 6; did you mean \"tuesday\"?"}
  }
}
HTTPcodeMeaning
400validation_failedThe request is malformed; fields says which inputs and why.
401unauthorizedMissing, invalid or revoked API key or session.
403forbiddenAuthenticated, but the key’s scopes or workspace membership don’t allow it.
403limit_exceededA plan limit was reached; limit and value say which.
404not_foundDoesn’t exist, or belongs to another workspace.
409conflictState conflict, e.g. enabling a job that isn’t disabled.
409idempotency_conflictIdempotency-Key reused with a different body.
429rate_limitedToo many requests; wait for Retry-After.
500internalA bug. Report it with the request ID.
503unavailableThe server is starting or a dependency is down; retry.

Pagination

List endpoints return a page plus a cursor:

{"data": [ … ], "next_cursor": "eyJpZCI6InJ1bl8wMUo5…"}

Pass ?cursor=<next_cursor> for the next page and ?limit= to set the page size (default 50, max 200). next_cursor is null on the last page.

Idempotent creates

POST endpoints that create things accept an Idempotency-Key header. Repeating the same request with the same key within 24 hours returns the original response instead of creating a duplicate. Reusing the key with a different body returns 409 idempotency_conflict.

Rate limits

CallerDefault limit
API key or session600 requests per minute (limits.api_rate_limit_per_minute)
Unauthenticated schedule endpoints60 requests per minute per IP

Responses include RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers.