Skip to content
cronpad
API reference / Jobs API

Jobs API

Jobs are the core resource: a schedule, a sink, and policies for retries, misfires, overlap and auto-disable. All endpoints are workspace-scoped through your API key.

The job object

{
  "id": "job_01J9ZK3Q…",
  "name": "nightly-report",
  "description": "Builds the daily revenue report",
  "tags": ["reports"],
  "schedule": {
    "expr": "every day at 9am",
    "kind": "natural",
    "canonical": "FREQ=DAILY;BYHOUR=9;BYMINUTE=0;BYSECOND=0",
    "description": "At 09:00 every day",
    "timezone": "Asia/Kolkata",
    "starts_at": null,
    "ends_at": null,
    "count": null
  },
  "sink": {"kind": "webhook", "config": {"url": "https://api.example.com/reports", "method": "POST"}},
  "retry": {"max_attempts": 3, "backoff": "exponential", "initial_interval": "10s", "max_interval": "10m", "multiplier": 2, "jitter": 0.2},
  "timeout": "30s",
  "sla_threshold": null,
  "misfire_policy": "fire_once",
  "overlap_policy": "allow",
  "auto_disable_threshold": null,
  "state": "active",
  "disabled_reason": null,
  "consecutive_failures": 0,
  "next_run_at": "2026-11-11T03:30:00Z",
  "next_runs": ["2026-11-11T03:30:00Z", "2026-11-12T03:30:00Z", "2026-11-13T03:30:00Z"],
  "last_run_at": "2026-11-10T03:30:00Z",
  "last_run_status": "succeeded",
  "version": 3,
  "created_at": "2026-10-16T08:00:00Z",
  "updated_at": "2026-10-20T11:12:00Z"
}

null for sla_threshold and auto_disable_threshold means “use the server default”. next_runs is included by GET /jobs/{id}.

Create a job

POST /api/v1/jobs

{
  "name": "nightly-report",
  "schedule": {"expr": "every day at 9am", "timezone": "Asia/Kolkata"},
  "sink": {"kind": "webhook", "config": {"url": "https://api.example.com/reports"}},
  "retry": {"max_attempts": 5},
  "timeout": "30s",
  "tags": ["reports"]
}

Only name, schedule.expr and sink are required. Omitted fields take server defaults. Partial retry objects are merged with the defaults.

Response 201 is the job object plus, once only, "signing_secret": "whsec_…" for webhook jobs.

Send an Idempotency-Key header to replay the same create response for 24 hours within a workspace. Reusing the key with a different payload returns 409 idempotency_conflict.

Errors: 400 validation_failed (bad schedule, unknown secret, URL not allowed, sink disabled on this server), 403 limit_exceeded (job count or minimum interval).

List jobs

GET /api/v1/jobs?state=active&tag=reports&q=report&limit=50

QueryDescription
stateactive, paused, disabled, completed
tagJobs with this tag
qCase-insensitive match on name and description

Get a job

GET /api/v1/jobs/{id}, including next_runs and 24-hour stats_24h (runs, succeeded, failed, sla_breached, p95_start_lag_ms).

Update a job

PATCH /api/v1/jobs/{id} with any subset of the create fields. The job’s version increases, queued occurrences of the old version are dropped, and the next fire time is recalculated from now.

Delete a job

DELETE /api/v1/jobs/{id} → 204. The job stops immediately; its run history remains until it ages out.

Change state

EndpointFromTo
POST /api/v1/jobs/{id}/pauseactivepaused
POST /api/v1/jobs/{id}/resumepausedactive (next future occurrence)
POST /api/v1/jobs/{id}/enabledisabledactive, failure counter reset

Calling from any other state returns 409 conflict.

Run now

POST /api/v1/jobs/{id}/trigger → 202 with {"run_id": "run_…"}. Creates a run with trigger = manual that starts immediately. The schedule is unaffected. Works on paused jobs too.

Test the sink

POST /api/v1/jobs/{id}/test → 200

{"outcome": "success", "status_code": 200, "duration_ms": 87, "response_body": "{\"ok\":true}", "request_summary": "POST https://api.example.com/reports (231 bytes)"}

Executes the sink once, synchronously, without recording a run or affecting counters. You can also test an unsaved config with POST /api/v1/jobs/test and a full job body.

Rotate the signing secret

POST /api/v1/jobs/{id}/rotate-signing-secret → 200 with the new signing_secret. Both secrets sign requests for 24 hours.