Guides

Delivery Scheduling

Publish an event now, but defer fan-out until a future time — useful for anything that needs to land at a specific moment rather than the instant you call the API.

Publishing for the future

Add an optional deliver_at field to the publish request — everything else about publishing stays the same.

curl
curl -X POST https://ingest.staging.lobthat.com/api/2026-06/app_01J0ZXYZ/events \
  -H "Authorization: Bearer whk_<your_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "order.created",
    "payload": { "order_id": "ord_123" },
    "deliver_at": "2026-08-01T09:00:00Z"
  }'
Stored immediately, delivered later
The Event is written durably the moment you publish it — deliver_at only defers when fan-out happens, not whether the event exists. If you never set it, behavior is exactly today's: fan-out happens right away.
RFC 3339, future, within 30 days
deliver_at must be a valid RFC 3339 timestamp, strictly in the future, and no more than 30 days out. Any violation — malformed, in the past, or too far out — is rejected at publish time with 400 invalid_deliver_at, before anything is written.

Cancelling before it fires

Event scheduled POST .../events/{event_id}/cancel fan-out never happens

Cancellation only works on an event that's still waiting — once fan-out has actually started, it's too late. A "Cancel Event" button lives right in the dashboard's countdown panel for a scheduled event, behind a destructive-action confirmation.

SituationResult
already_cancelledEvent was already cancelled — cancelling twice is rejected.
already_fanned_outFan-out has already happened (including an immediate, non-scheduled publish) — too late to cancel.
not_scheduledThe event was never given a deliver_at in the first place.
cancellation_lockedToo close to deliver_at — see the cancel-lockout window below.
Cancellation is refused in the final moments before delivery — not just in the dashboard, but enforced on the backend too, so a direct API call can't sneak past it either. Without this, cancelling in the last second or two would race the worker actually picking the job up: the cancellation could land just after the worker already committed to delivering, so the event would fire anyway despite showing as "cancelled." The exact cutoff is the cancel-lockout window, configurable per Application (see below).

How it shows up in Delivery Logs

A scheduled event has its own visible states, distinct from the normal delivered/retrying/failed lifecycle that only applies once fan-out has actually happened.

StatusMeaning
scheduledWaiting for deliver_at — no delivery attempts exist yet, and none will until then.
overduedeliver_at has passed with no delivery attempt recorded yet — distinct from a healthy event that just hasn't been picked up in the last few seconds. See the overdue threshold below.
cancelledCancelled before fan-out — will never be delivered.
pendingNot scheduled at all, just hasn't fanned out yet (today's existing meaning, unchanged).
fanned_outFan-out has happened — normal delivery/retry tracking takes over from here.
Under the hood, deferred fan-out uses the queue's own native delayed-processing option — there's no separate scheduler or cron sweep involved, so a scheduled event fires exactly once its time arrives, not on the next periodic check.

Application-level settings

Two related settings, both scoped per Application and saved together via a single "Save Settings" action on the Application's Delivery Scheduling settings card.

Overdue threshold
How many minutes past deliver_at a scheduled event is allowed to sit with zero delivery attempts before it's reported overdue instead of scheduled. Default 10 minutes, configurable 1–1440.
Cancel lockout window
How many seconds before (and after) deliver_at cancellation is refused. Default 30 seconds, configurable 0–3600 — set to 0 to disable the lockout entirely. The same check naturally covers an already-overdue event too, with no special-casing needed.
Dashboard → Application → Application Settings → Delivery Scheduling