Launch & ops
Approval timeouts
Time-boxed human gates: policy windows, auto-reject or escalate to the next stage, cron with CRON_SECRET, audit and webhooks.
Time-boxed approval windows
Human approval queues are a common bottleneck. Time-boxed windows (with automatic reject or escalation to the next routing stage) keep workloads moving and match operational patterns recommended by vendors such as Strata.io for identity and access governance SLAs.
Ruleset fields
On each routing stage (same objects as stage_index, role, tier), you may set:
| Field | Type | Range | Description |
|---|---|---|---|
approval_timeout_seconds | integer | 60–2 592 000 | When the stage holds the active approval_token, decide by this wall-clock window. |
on_approval_timeout | string | optional | reject (default if omitted) or escalate_next. Requires approval_timeout_seconds on the same stage when set. |
Omit approval_timeout_seconds for stages with no deadline (indefinite pending until a human acts).
What the runtime stores
When evaluations create approval_stages, each row copies:
timeout_seconds_snapshot— fromapproval_timeout_seconds(or null).timeout_action_snapshot— fromon_approval_timeoutwhen a timeout is configured (or null; treated as reject at enforcement time).
Only the stage that currently has an approval_token gets approval_deadline_at set (now + timeout in UTC). When a stage completes and the next pending stage receives a token, notifyApproversForEvaluation sets that row’s deadline from its snapshot.
Enforcement cron
Timeouts are applied by a server cron, not inline on every page view:
POST /api/cron/approval-timeouts- Header:
Authorization: Bearer <CRON_SECRET>(same secret asreplay-webhookautomation). - Optional JSON body:
{ "limit": 40 }(default 40, capped at 200 per invocation).
Configure your host (Vercel Cron, GitHub Actions, etc.) to call this on a short interval (for example every 1–5 minutes). If the job never runs, deadlines never fire.
Set CRON_SECRET in .env / host secrets (see .env.example).
Behavior summary
on_approval_timeout: "reject" (or omitted)
- Current stage →
rejected; otherpendingstages →skipped. - Evaluation →
rejected. - Integrator webhook (if
webhook_urlwas set):terminal_outcomewithrejectionReason: "approval_timeout". - Audit:
approval_timeoutwith payloadaction: "reject". - Run bundle: append human decision receipt as rejected for that stage.
on_approval_timeout: "escalate_next"
- Current stage →
skipped(timed out without a human decision). - Next
pendingstage with higherstage_indexreceives a new token andapproval_deadline_atfrom its snapshot. - Approvers are notified (email / Slack / Teams) like a normal handoff; integrator webhook
approval_required/ pending-human flow as today. - Audit:
approval_timeoutwithaction: "escalate_next".
If there is no next pending stage, behavior matches reject: evaluation rejected, audit approval_timeout_escalation_exhausted, webhook rejectionReason: "approval_timeout_escalation_exhausted".
Approver UI
The hosted /approve/[token] page shows Decision window (local time) when approval_deadline_at is set.
Dashboard templates
Built-in examples:
- Single-stage — 48h then auto-reject
- Multi-stage — escalate after 24h
Related docs
- Operations runbook — cron setup
- Production readiness — env checklist
- Governance analytics — latency and outcomes