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.

FieldPurpose
cron or intervalWhen to fire (exactly one required)
handlerRouting key. Matches your worker’s registered handler name
deliveryHow to deliver: pull (worker) or push (HTTP)
payloadData passed to every job this schedule creates
timeoutSeconds before an execution is considered timed out (default: 30)
max_retriesHow many times to retry on failure (default: 3)
runsStop after this many jobs (optional)
stop_atStop 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:

StatusMeaning
pendingCreated, waiting to be processed by the internal queue
admittingBeing picked up by the internal dispatcher
queuedIn the dispatch queue, waiting for its run_at time
readyDue for execution and available to be claimed by a pull worker. Push jobs skip this state.
runningActively being executed
completedFinished successfully
failedFailed and retries exhausted
retryingFailed but has retries remaining. Waiting for next attempt
cancelledCancelled 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 jobOne-off job
Created byA schedule (automatically)You (via POST /v1/jobs)
schedule_idUUID of parent schedulenull
TimingDetermined by schedule’s cron/intervalscheduled_for, delay, or immediate
handlerInherited from scheduleSet 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
StatusMeaning
pendingCreated, not yet started
runningIn progress. Your handler is executing or push request is in-flight
completedHandler returned successfully (or endpoint returned 2xx)
failedHandler threw an error (or endpoint returned 4xx/5xx)
timeoutExecution exceeded the job’s timeout without reporting a result
FieldDescription
triggerWhy this execution exists: system (first attempt), system_retry (automatic retry), manual (reserved)
duration_msHow long the execution took (null if not completed)
resultReturn value from your handler (if completed with data)
errorError message (if failed, truncated to 4KB)
response_codeHTTP 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.