Skip to content

Schedules, Jobs & Executions

Chronos has three primitives. Understanding how they relate is the key to using the API effectively.

Schedule ──spawns──▶ Job ──creates──▶ Execution
(pattern) (work) (attempt)
  • Schedule: A recurring pattern that produces jobs on a cadence. Think of it as a standing instruction: “run this every hour” or “run this at 9am on weekdays.”
  • Job: A unit of work to execute. Created by a schedule (recurring) or directly via the API (one-off).
  • Execution: A single attempt to run a job. If a job fails and has retries remaining, a new execution is created for the next attempt.

One schedule produces many jobs. One job may have multiple executions (if retries are needed).

A schedule defines when and how work should happen.

Field Purpose
cron or interval When to fire (exactly one required)
handler Routing key. Matches your worker’s registered handler name
delivery How to deliver: pull (worker) or push (HTTP)
payload Data passed to every job this schedule creates
timeout Seconds before an execution is considered timed out (default: 30)
max_retries How many times to retry on failure (default: 3)
runs Stop after this many jobs (optional)
stop_at Stop after this time (optional)
active ──────▶ completed (runs limit reached or stop_at passed)
└──────────▶ archived (manual, via POST /v1/schedules/:id/archive)
  • active: Producing jobs on schedule.
  • completed: Finished naturally. Reached its runs limit or passed stop_at.
  • archived: Stopped manually. All pending jobs for this schedule are cancelled.

Archiving is the only manual lifecycle action. Completion happens automatically.

Interval schedules compute the next run from the scheduled time, not from wall-clock:

Schedule: every 1 hour
First run scheduled for: 10:00:00
Job spawned at (actual): 10:00:03 (3s late due to system load)
Next run computed from: 10:00:00 (not 10:00:03)
Next run scheduled for: 11:00:00

This prevents drift. Jobs don’t slide later over time.

A job is a discrete unit of work. It’s either spawned by a schedule or created directly as a one-off.

pending → admitting → queued → ready → running → completed
│ │
│ ├──▶ failed (retries exhausted)
│ │
│ └──▶ retrying → (back to pending)
└──▶ cancelled (schedule archived)

What each status means:

Status Meaning
pending Created, waiting to be processed by the internal queue
admitting Being picked up by the internal dispatcher
queued In the dispatch queue, waiting for its run_at time
ready Due for execution and available to be claimed by a pull worker. Push jobs skip this state.
running Actively being executed
completed Finished successfully
failed Failed and retries exhausted
retrying Failed but has retries remaining. Waiting for next attempt
cancelled Cancelled because its parent schedule was archived

From your perspective as a developer, the statuses you’ll interact with are: pending (just created), running (your handler has it), completed, failed, and retrying.

Recurring job One-off job
Created by A schedule (automatically) You (via POST /v1/jobs)
schedule_id UUID of parent schedule null
Timing Determined by schedule’s cron/interval scheduled_for, delay, or immediate
handler Inherited from schedule Set on the job directly

Each time a job produces a new execution (first attempt or retry), attempt_count increments. You don’t need to track this yourself. ctx.attempt in your handler tells you which attempt this is.

An execution is a single attempt to run a job. It’s the lowest-level primitive: the record of what actually happened.

pending → running → completed
→ failed
→ timeout
Status Meaning
pending Created, not yet started
running In progress. Your handler is executing or push request is in-flight
completed Handler returned successfully (or endpoint returned 2xx)
failed Handler threw an error (or endpoint returned 4xx/5xx)
timeout Execution exceeded the job’s timeout without reporting a result
Field Description
trigger Why this execution exists: system (first attempt), system_retry (automatic retry), manual (reserved)
duration_ms How long the execution took (null if not completed)
result Return value from your handler (if completed with data)
error Error message (if failed, truncated to 4KB)
response_code HTTP status code (push delivery only)

When a job fails and retries remain:

  1. The current execution is marked failed
  2. The job transitions to retrying
  3. After the backoff delay, a new execution is created
  4. The new execution has trigger: "system_retry"

Each retry is a distinct execution with its own execution_id. Use job_id for side effects that must happen once per job, and use execution_id for per-attempt logs, metrics, and correlation.

Schedule "Hourly sync" (cron: 0 * * * *)
├── Job (10:00) ── Execution #1 ✓ completed
├── Job (11:00) ── Execution #1 ✗ failed
│ └─ Execution #2 ✓ completed (retry succeeded)
└── Job (12:00) ── Execution #1 ✓ completed
One-off Job "Send welcome email"
└── Execution #1 ✓ completed

Each execution attempt counts toward your plan’s monthly execution limit.