All docs
Docs · Reference
Scheduled
Run work at a future time
Cron turns the clock into a tick; scheduled turns a single instant into one event. A rule enqueues "run this, not before T" and walks away; the chassis fires it when T arrives.
Where cron is recurring and tenant-wide, scheduled is a
one-shot you place from inside a flow — a follow-up email next Tuesday, a
reminder in two weeks, the next installment at 08:00.
Enqueue with txco://schedule
Any rule can place an event. The tenant is the request-pinned tenant, so the event always fires back into the tenant that scheduled it.
EXEC "txco://schedule" WITH
idempotency_key = "drip:reader@example.com:37", # dedup + cancel handle
schedule_at = "2026-06-30T08:00:00Z", # RFC3339; "not before"
payload = .job # any JSON object idempotency_keyis scoped to the tenant. Re-running with the same key while the event is still pending reschedules in place (new time/payload) — no duplicate. Once it has fired, the key is spent.- Cancel a pending event:
EXEC "txco://schedule" WITH idempotency_key = "…", cancel = true.
Firing
At its due time the event is delivered into your tenant’s _scheduled stack (define one to receive them — the stack’s existence is the
subscription, like _cron). Your payload rides on @scheduled.payload:
# _scheduled/0/route.txcl
WHEN @scheduled.payload.kind == "drip"
EXEC "txco://route" WITH stack = "send-drip" | Field | Meaning |
|---|---|
@scheduled.payload.* | the JSON object you enqueued |
@scheduled.idempotency_key | the key it was scheduled under |
@scheduled.event_id | store row id |
@scheduled.fired_at | fire instant, UTC RFC3339 |
@scheduled.tenant / @scheduled.node | subscriber / firing chassis |
Delivery
Events live in a durable table (--scheduled-store, default sqlite). The
poller claims each due row before firing — a status flip, so a slow fire
can’t double-send — and a claim left stranded by a crash is retried after --scheduled-stale-after. A response timeout is treated as fired (at-most-once
bias — the work likely ran). Outcomes are visible in the logs and trace only.
Flags
| Flag | Default | Meaning |
|---|---|---|
--scheduled-store | sqlite | Backend for the scheduled_events table |
--scheduled-db-path | ./chassis/data/scheduled.db | Bundled SQLite store path |
--scheduled-period | 20 | Seconds between poll passes (worst-case firing latency past schedule_at) |
--scheduled-max-inflight | 32 | Concurrent fires per pass |
--scheduled-stale-after | 600 | Seconds before an abandoned claim is retried |
--scheduled-retention | 604800 | Seconds to keep terminal (done/failed) rows |
Enable by adding scheduled to --personalities.