Skip to content

Automation API

All automation endpoints are project-scoped. The concept docs live at Automation rules; this page is the REST surface the dashboard's Automation area uses.

Mutating endpoints require a CSRF token (see Authentication). Role enforcement is destructiveness-aware: a rule whose actions are only notify needs project operator, while a rule carrying a destructive action (retry, cancel) needs project admin plus a fresh second-factor check (the same step-up gate as the other sensitive actions; expect 403 mfa_reverify_required when the sudo window has lapsed). Actions the grammar knows but the runner does not execute (webhook, revoke, purge, pause_schedule) are rejected fail-closed with 422 at create and update time.

GET /api/v1/projects/{slug}/automation/rules

Role: viewer. Returns {"items": [Rule, ...]}.

A rule object:

{
"id": "5f0b7f2e-...",
"project_id": "6b2d25e3-...",
"name": "Retry failed invoice runs",
"is_enabled": true,
"dry_run": false,
"trigger": "task.failed",
"conditions": {"task_name": "billing.run_daily_invoices"},
"actions": [{"type": "retry", "max_retries": 2}],
"max_executions_per_window": 20,
"window_seconds": 300,
"cb_tripped": false,
"cb_execution_count": 3,
"created_by": "26573b1a-...",
"created_at": "2026-07-16T12:00:00Z",
"updated_at": "2026-07-16T12:00:00Z"
}

cb_tripped / cb_execution_count expose the rolling-window circuit breaker: when a rule exceeds max_executions_per_window inside window_seconds, the breaker trips and the rule degrades to notify-only. It clears itself: each admission attempt ages out executions older than the window, and once fewer than the limit remain the next admission both executes and resets the flag. There is no operator reset control, so do not go looking for one.

GET /api/v1/projects/{slug}/automation/rules/{rule_id}

Role: viewer.

POST /api/v1/projects/{slug}/automation/rules

Role: operator for notify-only rules and admin when any action is destructive. A browser-session request for a destructive rule also requires a fresh second factor; bearer-authenticated callers follow their API-key authority. Returns 201 with the created rule.

Body fields mirror the rule object: name, trigger (task.failed, task.succeeded, task.retried, task.orphaned, worker.offline, schedule.misfired), optional conditions (engine, queue, task name, exception, priority, runtime, or an issue fingerprint), actions, dry_run, is_enabled, and the breaker window (max_executions_per_window, window_seconds).

PATCH /api/v1/projects/{slug}/automation/rules/{rule_id}

Same role rules as create, evaluated against both the old and post-update rule shape. An edit that touches or introduces a destructive action requires admin; browser-session calls also require a fresh second factor.

DELETE /api/v1/projects/{slug}/automation/rules/{rule_id}

Role: operator for notify-only rules, admin for rules with destructive actions. Returns 204.

POST /api/v1/projects/{slug}/automation/rules/{rule_id}/reset-circuit

Clears the breaker so the rule can fire again, and writes an automation.rule.circuit_reset audit row. Re-arming a destructive rule requires admin; browser-session calls also require a fresh second factor. Returns the refreshed rule.

GET /api/v1/projects/{slug}/automation/settings
PUT /api/v1/projects/{slug}/automation/settings

The settings object carries the per-project kill switch:

{"automation_enabled": true}

When false, the rule engine loads no rules for the project, so nothing fires regardless of each rule's own is_enabled state. Every firing (and every breaker trip, skip, and auto-disable) lands on the HMAC-chained audit log.