These endpoints are system-only. They are authenticated with a shared
secret: every /api/v1/cron/* route accepts CRON_SECRET or the legacy
CRON_TOKEN alias (whichever is set), and the legacy /api/cron/tick accepts
either as well. They are intended to be invoked by the in-process maintenance
scheduler, AWS EventBridge Scheduler, or an equivalent external cron.During pilot / initial deployment, EventBridge is typically not wired.
That means:
/api/cron/tick exists and is callable, but is effectively unused
until a scheduler starts hitting it.
- Alert rules and report schedules will not fire automatically until a
caller invokes this endpoint.
- Features like Run Now (reports) and on-demand alert evaluation work
independently and do not require any cron wiring.
See EventBridge Setup for the setup
path (marked optional on purpose).
GET /api/v1/cron/export-interactions
Exports new interaction history rows for all active tenants as Hive-partitioned NDJSON files. Uses checkpoint tracking to export only rows created since the last run.
Intended to be called by a cron scheduler (e.g., Vercel Cron Jobs, Kubernetes CronJob). Processes up to 10,000 rows per tenant per invocation.
Authentication
Requires a CRON_SECRET token via either:
Authorization: Bearer <CRON_SECRET> header
X-Cron-Secret: <CRON_SECRET> header
File Layout
Each file contains newline-delimited JSON (NDJSON), compatible with Hive, Spark, Athena, and other data lake tools.
Response
Error Handling
If export fails for one tenant, other tenants continue processing. Failed tenants include an error field and their checkpoint is marked as failed for retry on the next run.
In production, replace the local filesystem writes with S3 uploads via @aws-sdk/client-s3. The file paths follow the same Hive-style partitioning scheme.
GET /api/v1/cron/cleanup
Periodic data cleanup that removes expired suppressions, purges interaction history, interaction summaries, and decision traces according to each tenant’s retention configuration, and ensures future database partitions exist.
Authentication
Requires CRON_SECRET via Authorization: Bearer <secret> or X-Cron-Secret header. If CRON_SECRET is not set in the environment, the endpoint is fail-closed and rejects every request with 401.
Cleanup Operations
| Operation | Description |
|---|
| Expired suppressions | Deletes any suppression entry whose expiry timestamp is in the past. Event-driven; does not consult per-tenant retention settings. |
| Per-tenant history purge | For every tenant, applies that tenant’s retention settings — history days, summary days, decision-trace days — together with the legalHold flag. Purges interaction history, interaction summary rows (all four period types — daily, weekly, monthly, all-time), and decision-trace rows. A tenant with legalHold = true is skipped per-table and surfaced in legalHoldSkips. |
| Expired variant assignments | Per tenant, deletes experiment variant-assignment rows past their expiry, counted in expiredVariantAssignments. |
| Aged audit logs | Per tenant, purges audit-log rows older than the retention window, counted in oldAuditLogs. |
| Published outbox events | Per tenant, removes already-published outbox rows, counted in publishedOutboxEvents. |
| Partition maintenance | Creates database partitions for the next 3 months (non-fatal if partitions are not configured). |
A failure inside one tenant’s purge is captured in perTenantErrors and does not abort the run — the cron will continue with the remaining tenants and still emit a 200 response.
Response
Error Codes
| Code | Reason |
|---|
401 | Invalid or missing CRON_SECRET |
500 | Cleanup operation failed |
GET /api/v1/cron/staging-janitor
Reaps leaked _flow_* staging tables — the per-run source/transform/branch
scratch tables the pipeline runtime materializes. Tables belonging to
in-flight runs are never dropped; only orphaned tables (the run row was
deleted, or the run finished before the retention cutoff) are removed. Tables
matching the _flow_* prefix but not the expected shape are counted as
unparseable and left in place.
Authentication
Requires CRON_SECRET (or the legacy CRON_TOKEN alias) via
Authorization: Bearer <secret>. Fail-closed with 401 when neither is set.
Configuration
| Env var | Default | Description |
|---|
FLOW_STAGING_RETENTION_HOURS | 24 | Staging tables for finished runs older than this window are dropped. In-flight runs are exempt regardless of age. |
Response
droppedTables is capped at the first 50 entries in the response; the full
list is written to the log.
Error Codes
| Code | Reason |
|---|
401 | Invalid or missing CRON_SECRET / CRON_TOKEN |
500 | Janitor run failed |
Invoked hourly by the in-process maintenance scheduler when a cron secret is
set (see Cron tier), and available to any
external scheduler (EventBridge, Kubernetes CronJob).
GET /api/v1/cron/approvals-expire
Sweeps every pending approval request and flips any that has aged past
APPROVAL_MAX_AGE_HOURS (default 168 hours = 7 days) to status = "expired".
The operation is a single bulk database write and is fully idempotent — running
it again immediately is a no-op because no rows match the cutoff anymore.
Authentication
Same CRON_SECRET shared-header pattern as /api/v1/cron/cleanup:
Authorization: Bearer <CRON_SECRET> header
X-Cron-Secret: <CRON_SECRET> header
Unset CRON_SECRET → the endpoint rejects all requests with 401 (fail-closed).
Configuration
| Env var | Required | Description |
|---|
CRON_SECRET | Yes | Shared secret. Fail-closed when unset. |
APPROVAL_MAX_AGE_HOURS | No | Maximum age in hours before a pending approval auto-expires. Default 168 (7 days). Invalid values fall back to the default. |
Recommended schedule
Every 15 minutes is plenty — the operation is O(rows-to-expire) at the
database with zero per-row work in Node. Once nothing is left to expire,
each invocation is essentially free.
Response
Error codes
| Code | Reason |
|---|
401 | Missing or invalid CRON_SECRET |
500 | Underlying database write failed (transient DB issue) |
GET /api/v1/cron/ai-autopilot
Decisioning Autopilot sweep. For every tenant it gathers experiment-promotion and model-drift
proposals, persists them as AiRecommendation rows (source: "autopilot"), then acts according
to the tenant’s aiAutopilot.mode:
suggest — inbox only (proposals created, nothing applied).
auto_gated — opens an ApprovalRequest for each proposal.
auto — applies the change and writes an audit entry.
Authentication
CRON_SECRET via Authorization: Bearer <secret> or X-Cron-Secret: <secret>. Fail-closed with
401 when CRON_SECRET is unset.
Response
GET /api/v1/cron/ai-sentinel
Decision Sentinel sweep (recommended every 30 min). Computes suppression_rate and
empty_candidate_rate per tenant, writes System Health alerts on
breach, and — only for tenants with aiAutopilot.sentinelAutoPause = true — pauses active flows on
a hard breach.
Authentication
CRON_SECRET via Authorization: Bearer <secret> or X-Cron-Secret: <secret>. Fail-closed with
401 when CRON_SECRET is unset.
Response
Complete cron job catalog
Every /api/v1/cron/* job uses the same CRON_SECRET (or legacy CRON_TOKEN) bearer auth and
fails closed when the secret is unset. Jobs documented in full above are cross-linked; the rest
share the same auth and an { "ok": true, ... } response shape.
| Job | Verb | Purpose |
|---|
ai-autopilot | GET | Autopilot proposal sweep (documented above). |
ai-sentinel | GET | Decision Sentinel breach sweep (documented above). |
approvals-expire | GET | Expire pending approvals past APPROVAL_MAX_AGE_HOURS (documented above). |
backfill-direction-adaptations | POST | One-shot backfill of inbound/outbound model_adaptations from historical interaction_history. |
cleanup | GET | Retention purge of history/summaries/traces + partition maintenance (documented above). |
drain-queues | GET/POST | Drain the BullMQ queues with ephemeral workers (see Drain Queues). |
dsar-purge | GET | Purge tenant data past the RetentionConfig window (DSAR / right-to-erasure). |
engagement-health-recompute | GET | Nightly recompute of CustomerEngagementHealth from the last 90 days. |
export-interactions | GET | Hive-partitioned NDJSON export of new interaction history (documented above). |
fairness-recheck | GET | Re-run the fairness gate for tenants with continuous recheck; auto-pause on drift. |
flow-scheduler-tick | GET | Dispatch due schedule- and file-arrival-triggered pipelines (also runs in-process). |
gitops-drift-check | GET | Nightly compare of live resources vs the last-applied GitOps YAML snapshot. |
outbox-reaper | GET | Reset outbox_events rows stuck in processing past the staleness window back to pending. |
scheduled-retrains | GET | Trigger due model retrains and apply evidence decay to adaptations. |
siem-ship | GET | Push recent AuditLog rows to the configured SIEM. |
staging-janitor | GET | Reap orphaned _flow_* staging tables (documented above). |
system-health-purge | GET | Delete SystemHealthAlert rows past retention (pinned rows are kept). |
POST /api/cron/tick
System cron endpoint that evaluates every enabled alert rule
and fires every due report schedule for every
tenant in a single pass.
The intended automated caller is AWS EventBridge, invoking the endpoint at
whatever cadence matches your windowMinutes values (every 1–5 minutes is
typical). See EventBridge Setup for the
wiring path.
Pilot deployments: During pilot / initial rollout, EventBridge is
typically not wired. The endpoint still exists and can be invoked on
demand via curl with a valid CRON_TOKEN — useful for development,
smoke testing, or one-off evaluation pushes. Without an external caller
hitting it on a cadence, alerts and scheduled reports do not fire
automatically; use the UI’s Run Now / Test buttons or the
per-entity /run-now APIs for immediate delivery.
Authentication
Requires a CRON_TOKEN via either:
x-cron-token: <CRON_TOKEN> header (preferred)
x-cron-secret: <CRON_TOKEN> header (legacy, same value)
Authorization: Bearer <CRON_TOKEN> header
Unset CRON_TOKEN → the endpoint rejects all requests with 401 (fail-closed).
Example
Response
Error Isolation
Per-tenant and per-rule errors are caught and reported in the errors array;
a failing tenant does not abort the tick for other tenants:
ok is true only when errors is empty.
Environment Variables
| Variable | Required | Description |
|---|
CRON_SECRET | Recommended | Shared secret for /api/v1/cron/* maintenance endpoints |
CRON_TOKEN | Required only when /api/cron/tick is used | Shared secret for /api/cron/tick alert + schedule evaluation. Falls back to CRON_SECRET when unset. Leave unset during pilot if you’re not wiring a scheduler. |
APPROVAL_MAX_AGE_HOURS | No | Cutoff age (in hours) for /api/v1/cron/approvals-expire. Default 168 = 7 days. |
When you wire an automated scheduler, match the cadence to the workload:
- tick: every 1–5 minutes (matches the smallest
windowMinutes across your alert rules)
- export-interactions: Every hour or every 6 hours depending on data volume
- cleanup: Once daily (e.g.,
0 3 * * *)