Jobs
A job is the unit of scheduling: when to run (a cron spec, or a single startDate for one-time jobs), what to pass (data), and how to run it (executorId). Jobs live inside a project.
Job object
{
"id": 1,
"accountId": 123,
"projectId": 456,
"executorId": 789,
"spec": "0 0 2 * * *",
"data": "{\"action\": \"cleanup\", \"target\": \"logs\"}",
"startDate": "2026-02-01T00:00:00Z",
"endDate": "2026-12-31T23:59:59Z",
"lastExecutionDate": "2026-02-01T00:00:00Z",
"retryMax": 3,
"timezone": "UTC",
"timezoneOffset": 0,
"status": "active",
"executionId": "a1b2c3...",
"dateCreated": "2026-01-15T10:30:00Z",
"dateModified": null,
"createdBy": "user123",
"modifiedBy": null,
"deletedBy": null
}
| Field | Description |
|---|---|
projectId | Required. Must belong to your account. |
executorId | Required. Must belong to your account. |
timezone | Required. An IANA zone name such as UTC or America/New_York (Local is rejected). Scheduling is computed in this zone. |
spec | Cron expression (see below). Leave empty for a one-time job. |
startDate | RFC3339. Required when spec is empty; otherwise the earliest time the job may fire. Must not be in the past. |
endDate | RFC3339, optional. The job stops firing after this time. Must not be in the past or before startDate. |
data | A JSON-encoded string passed to the executor. Up to 3 KB by default; 1 MB with the increased_job_payload_size_to_1mb account feature. |
retryMax | Retries after a failed execution. Up to 3 by default; up to 15 with the increased_retry_max_by_five feature. |
status | active (default) or inactive (paused). |
timezoneOffset | Optional integer stored with the job. Scheduling uses timezone; this field is informational. |
createdBy / modifiedBy / deletedBy | Actor identifiers; required on create / update / delete respectively. |
lastExecutionDate, executionId | Maintained by the scheduler. |
Fields with zero values are omitted from responses.
Cron format
Specs are parsed by robfig/cron v1, which uses six fields with a leading seconds field:
┌───────────── second (0 - 59)
│ ┌───────────── minute (0 - 59)
│ │ ┌───────────── hour (0 - 23)
│ │ │ ┌───────────── day of month (1 - 31)
│ │ │ │ ┌───────────── month (1 - 12)
│ │ │ │ │ ┌───────────── day of week (0 - 6, Sunday = 0)
* * * * * *
| Expression | Meaning |
|---|---|
0 0 2 * * * | Every day at 02:00 |
0 */15 * * * * | Every 15 minutes |
0 0 9 * * 1-5 | 09:00 Monday to Friday |
0 0 0 1 * * | Midnight on the first of each month |
Descriptors are also accepted: @yearly/@annually, @monthly, @weekly, @daily/@midnight, @hourly, and intervals such as @every 5m or @every 1h30m. A spec that does not parse returns 400 job spec is not valid.
A standard five-field cron expression is interpreted with the first field as seconds, so 0 2 * * * means "second 0 of every minute of 02:00", not "02:00 daily". Always write six fields.
Endpoints
| Method | Path | Scope | Notes |
|---|---|---|---|
GET | /jobs | read | Paginated; optional projectId filter |
POST | /jobs | write | Body is a JSON array of 1–100 jobs → 202 + request ID |
GET | /jobs/{id} | read | |
PUT | /jobs/{id} | write | Full replace; modifiedBy required |
DELETE | /jobs/{id} | write | Body {deletedBy} → 204 |
GET | /async-tasks/{id} | read | Result of a POST /jobs request |
Create jobs
POST /jobs takes an array, even for a single job (a non-array body returns 422). Every job needs projectId, executorId, timezone, createdBy, and either spec or startDate.
curl -X POST "https://api.scheduler0.com/api/v1/jobs" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '[
{
"projectId": 456,
"executorId": 789,
"spec": "0 0 2 * * *",
"data": "{\"action\": \"backup\", \"database\": \"production\"}",
"retryMax": 3,
"timezone": "UTC",
"createdBy": "user123"
}
]'
The batch is validated synchronously — an invalid spec, unknown project or executor, a startDate in the past, an oversized data payload or a retryMax above your limit returns 400 with the reason. Once validation passes the insert happens asynchronously: the response is 202 Accepted with the request ID in data and a Location header pointing at the async task:
{ "success": true, "data": "5f3c9e2a-..." }
Location: /async-tasks/5f3c9e2a-...
One-time jobs
Omit spec and set startDate. The job fires once at startDate (interpreted in timezone).
[
{
"projectId": 456,
"executorId": 789,
"startDate": "2026-03-01T08:00:00Z",
"timezone": "UTC",
"data": "{\"orderId\": 42}",
"createdBy": "user123"
}
]
Check the result
GET /async-tasks/{id} returns the task for the request ID. If the batch is still being processed the call blocks until it finishes, so one call is normally enough.
curl "https://api.scheduler0.com/api/v1/async-tasks/5f3c9e2a-..." \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID"
{
"success": true,
"data": {
"id": 12,
"requestId": "5f3c9e2a-...",
"input": "[...]",
"output": "[...]",
"service": "job-service",
"state": 2,
"accountId": 123,
"dateCreated": "2026-01-15T10:30:00Z",
"dateModified": "2026-01-15T10:30:01Z"
}
}
state | Meaning |
|---|---|
0 | Not started |
1 | In progress |
2 | Success — output contains the created jobs |
3 | Failed — output contains the error |
List and get
# All jobs in the account
curl "https://api.scheduler0.com/api/v1/jobs?limit=10&offset=0&orderBy=date_created&orderByDirection=desc" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID"
# Jobs in one project
curl "https://api.scheduler0.com/api/v1/jobs?projectId=456&limit=10&offset=0" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID"
| Parameter | Description |
|---|---|
projectId | Optional filter |
limit | Default 10, maximum 100 (429 above that) |
offset | Default 0 |
orderBy | id, project_id, spec, date_created (default), date_modified, timezone, account_id, modified_by, deleted_by, executor_id, start_date, end_date, retry_max |
orderByDirection | asc or desc (default) |
The response is { "total", "offset", "limit", "jobs": [...] }. GET /jobs/{id} returns one job or 404.
Update a job
PUT /jobs/{id} is a full replacement, not a patch. Send the complete job: projectId and spec (or a future startDate) are needed to pass validation, and any of data, retryMax, startDate, endDate, status you leave out is reset to empty. timezone, timezoneOffset and executorId fall back to their existing values when omitted. modifiedBy is required (400 otherwise).
The cron spec and projectId cannot be changed by an update — the values you send must match, and to reschedule a job you delete and recreate it.
curl -X PUT "https://api.scheduler0.com/api/v1/jobs/1" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '{
"projectId": 456,
"spec": "0 0 2 * * *",
"data": "{\"action\": \"backup\", \"database\": \"production\"}",
"retryMax": 5,
"status": "active",
"modifiedBy": "user123"
}'
Pause and resume
Set status to inactive to stop a job from firing and back to active to resume it, sending the rest of the job as above.
curl -X PUT "https://api.scheduler0.com/api/v1/jobs/1" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '{
"projectId": 456,
"spec": "0 0 2 * * *",
"data": "{\"action\": \"backup\", \"database\": \"production\"}",
"retryMax": 3,
"status": "inactive",
"modifiedBy": "user123"
}'
Delete a job
curl -X DELETE "https://api.scheduler0.com/api/v1/jobs/1" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Secret-Key: YOUR_SECRET_KEY" \
-H "X-Account-ID: YOUR_ACCOUNT_ID" \
-d '{"deletedBy": "user123"}'
Returns 204.
Execution and retries
When a job fires, the scheduler records an execution in state scheduled, invokes the executor, and updates the execution to success or failed. A failed invocation is retried up to retryMax times; retryMax: 0 disables retries. Each execution has a uniqueId derived from the job, project, last execution date and next execution time, so a run is never processed twice across restarts or leader changes. See Executions.
Related
- Projects
- Executors — payload your code receives
- Executions — history, analytics, totals
- AI Job Scheduling — create jobs from natural language