Recurring Jobs API
Recurring jobs execute automatically on a cron schedule. You can manage schedules through this API or declaratively using the SDK's [JobConfig(CronSchedule = "...")] attribute on IRecurringJob implementations.
Base URL: https://api.zeridion.com
All endpoints require Bearer token authentication and are subject to rate limits.
PUT /v1/recurring/{id}
Create or update a recurring job schedule (upsert). The id is a stable identifier you provide (e.g., rjob_daily_cleanup). If a schedule with this ID exists, it is updated; otherwise a new one is created.
Request
PUT /v1/recurring/{id}
Authorization: Bearer <api_key>
Content-Type: application/json
Body
| Field | Type | Required | Description |
|---|---|---|---|
job_type | string | Yes | Job type to execute on each trigger |
cron_expression | string | Yes | Cron expression defining the schedule |
queue | string | No | Target queue. Default: "default" |
payload | object | No | Fixed JSON payload passed to each execution |
timezone | string | No | IANA timezone for cron evaluation. Default: UTC |
enabled | boolean | No | Whether the schedule is active. Default: true |
max_attempts | integer | No | Per-execution retry limit. Default: 3. On update, omitting this field keeps the existing value. |
timeout_seconds | integer | No | Per-execution timeout in seconds. Default: 1800. On update, omitting this field keeps the existing value. |
Example
{
"job_type": "MyApp.Jobs.CleanupExpiredSessions",
"cron_expression": "0 3 * * *",
"queue": "maintenance",
"timezone": "America/New_York",
"max_attempts": 3,
"timeout_seconds": 600,
"payload": { "retention_days": 30 }
}
Response
200 OK
{
"id": "rjob_daily_cleanup",
"job_type": "MyApp.Jobs.CleanupExpiredSessions",
"queue": "maintenance",
"cron_expression": "0 3 * * *",
"timezone": "America/New_York",
"enabled": true,
"last_run_at": null,
"next_run_at": "2026-03-25T07:00:00Z",
"max_attempts": 3,
"timeout_seconds": 600,
"created_at": "2026-03-24T15:00:00Z",
"updated_at": null
}
| Field | Type | Description |
|---|---|---|
id | string | The schedule identifier you provided in the URL |
job_type | string | Job type to execute |
queue | string | Target queue |
cron_expression | string | Cron schedule |
timezone | string | IANA timezone |
enabled | boolean | Whether the schedule is active |
last_run_at | string (ISO 8601) | When the schedule last triggered, or null |
next_run_at | string (ISO 8601) | Next scheduled trigger time, or null if disabled |
max_attempts | integer | Per-execution retry limit |
timeout_seconds | integer | Per-execution timeout |
created_at | string (ISO 8601) | When the schedule was first created |
updated_at | string (ISO 8601) | When the schedule was last modified, or null |
Errors
| Status | Code | Condition |
|---|---|---|
| 400 | invalid_cron_expression | The cron_expression could not be parsed. E.g., "not a cron". |
| 400 | invalid_timezone | The timezone is not a recognized IANA timezone. E.g., "Mars/Olympus_Mons". |
| 409 | recurring_job_type_conflict | A schedule with the same job_type already exists under a different ID. |
GET /v1/recurring
List all recurring job schedules for the authenticated project, ordered by creation time (ascending).
Request
GET /v1/recurring
Authorization: Bearer <api_key>
Response
200 OK
{
"data": [
{
"id": "rjob_daily_cleanup",
"job_type": "MyApp.Jobs.CleanupExpiredSessions",
"queue": "maintenance",
"cron_expression": "0 3 * * *",
"timezone": "America/New_York",
"enabled": true,
"last_run_at": "2026-03-24T07:00:00Z",
"next_run_at": "2026-03-25T07:00:00Z",
"max_attempts": 3,
"timeout_seconds": 600,
"created_at": "2026-03-24T15:00:00Z",
"updated_at": null
}
]
}
Returns an empty data array if no recurring jobs are configured.
DELETE /v1/recurring/{id}
Permanently delete a recurring job schedule. This is a hard delete — the schedule is removed entirely. To temporarily pause a schedule without deleting it, use PUT with enabled: false instead.
Request
DELETE /v1/recurring/{id}
Authorization: Bearer <api_key>
Response
200 OK
{
"deleted": true
}
Errors
| Status | Code | Condition |
|---|---|---|
| 404 | recurring_job_not_found | No schedule with this ID exists |
Cron Expression Format
Cron expressions use the standard 5-field format parsed by the Cronos library:
┌───────────── minute (0–59)
│ ┌───────────── hour (0–23)
│ │ ┌───────────── day of month (1–31)
│ │ │ ┌───────────── month (1–12)
│ │ │ │ ┌───────────── day of week (0–6, Sun=0)
│ │ │ │ │
* * * * *
Common examples
| Expression | Schedule |
|---|---|
* * * * * | Every minute |
0 * * * * | Every hour |
0 0 * * * | Daily at midnight |
0 9 * * 1-5 | Weekdays at 9:00 AM |
*/15 * * * * | Every 15 minutes |
0 0 1 * * | First day of month |
30 2 * * 0 | Sundays at 2:30 AM |
Timezone handling
By default, cron expressions are evaluated in UTC. Set the timezone field to an IANA timezone to evaluate in a local timezone (e.g., "America/New_York", "Europe/London"). This correctly handles daylight saving time transitions.
Disabling and Re-enabling
To pause a recurring schedule without deleting it, update it with enabled: false:
{
"job_type": "MyApp.Jobs.CleanupExpiredSessions",
"cron_expression": "0 3 * * *",
"queue": "maintenance",
"enabled": false
}
When disabled, next_run_at is set to null and the CronScheduler service skips this schedule. To resume, send another PUT with enabled: true.
SDK Auto-Registration
When the SDK starts, it calls POST /v1/workers/register with any recurring schedules discovered from [JobConfig(CronSchedule)] attributes. Each schedule is upserted with an ID derived from the job type (e.g., rjob_CleanupExpiredSessions). See the Recurring Jobs guide for more details.