Automations¶
Automations are declarative trigger → conditions → actions definitions that
drive workflow automation — a superset of the original env-var alerts. They are
evaluated by the consumer process, managed via the /api/automations endpoints,
and exposed as the *_automation MCP tools.
How evaluation works¶
When the consumer receives an event, it loads every enabled event automation
whose event_type matches, evaluates the automation's conditions against the
event, and — if they all pass — fires its actions. Every evaluation is
recorded in the append-only automation_runs log (metadata only; args, kwargs,
results, and tracebacks are never stored).
Periodic automations run on a schedule instead, evaluated by the consumer's
periodic loop (AUTOMATION_CHECK_SECONDS, default 5).
Anatomy of an automation¶
{
"name": "alert-on-failure",
"enabled": true,
"trigger_type": "event",
"event_type": "task-failed",
"conditions": [{"field": "name", "op": "eq", "value": "payments.charge"}],
"actions": [{"type": "log"}],
"cooldown_seconds": null,
"max_runs_per_window": null,
"window_seconds": null,
"circuit_breaker": null
}
Creating an automation¶
curl -X POST http://localhost:8000/api/automations \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "alert-on-failure",
"trigger_type": "event",
"event_type": "task-failed",
"conditions": [{"field": "name", "op": "eq", "value": "payments.charge"}],
"actions": [{"type": "slack_webhook", "webhook_url": "https://hooks.slack.com/..."}]
}'
Triggers¶
trigger_type |
Required fields | Fires on |
|---|---|---|
event |
event_type |
A matching Celery event (task-failed, task-succeeded, worker-offline, ...) |
periodic |
schedule_seconds |
A schedule; conditions evaluate against an empty event context |
Conditions¶
Conditions are {field, op, value} triples evaluated against the event. Field
paths support dots (e.g. request.id). All conditions must pass.
| Op | Behavior |
|---|---|
eq |
Field equals value |
neq |
Field does not equal value |
gt / gte |
Field greater than / greater-or-equal (numeric) |
lt / lte |
Field less than / less-or-equal (numeric) |
contains |
String contains value, or list/dict contains value |
matches |
Field (stringified) matches a regex |
in |
Field is in a list |
exists |
Field is present (non-null) |
Example — only alert for retried failures of one task on the high queue:
{
"conditions": [
{"field": "name", "op": "eq", "value": "payments.charge"},
{"field": "retries", "op": "gte", "value": 1},
{"field": "queue", "op": "in", "value": ["high", "critical"]}
]
}
Actions¶
| Type | Parameters |
|---|---|
log |
level, message |
slack_webhook |
webhook_url (or ALERT_WEBHOOK_URL), text, fields (dict of {label: value}) |
webhook |
url, payload (JSON) |
retry_task |
task_id (defaults to event uuid) |
execute_task |
name, args, kwargs, queue, countdown, eta, expires, priority |
revoke_task |
task_id (defaults to event uuid), terminate |
check_workers_offline |
webhook_url — scan for stale workers and alert |
Action params support {event.field} interpolation — the value is resolved
from the triggering event at fire time:
{
"type": "webhook",
"url": "https://example.com/hooks",
"payload": {"task_id": "{event.uuid}", "task_name": "{event.name}"}
}
Safety knobs¶
Prevent alert/action storms:
cooldown_seconds— after firing, wait at least this long before firing again.max_runs_per_window+window_seconds— fire at mostmax_runs_per_windowtimes perwindow_seconds.circuit_breaker—{"failure_threshold": N, "window_seconds": W}; skip actions (circuit_open) once the automation has fired N times within W seconds, and auto-close once the window rolls past.
Skipped evaluations are still recorded in automation_runs with a
details.skipped reason: "cooldown", "rate_limited", or "circuit_open".
Observability¶
- Run history:
GET /api/automations/{id}/runs/get_automation_runs. - Status + circuit state:
GET /api/automations/{id}/status/get_automation_statusreturns the automation, its latest run, and whether the circuit isopenorclosed. - Prometheus:
taskowl_automation_fired_totalandtaskowl_automation_skipped_totalcounters.
Worked example: retry with circuit breaker¶
- Retry
payments.chargeon failure, but never more than twice per hour and pause entirely if it fails 3 times within 5 minutes:
{
"name": "retry-payments",
"trigger_type": "event",
"event_type": "task-failed",
"conditions": [{"field": "name", "op": "eq", "value": "payments.charge"}],
"actions": [{"type": "execute_task", "name": "payments.charge", "countdown": 30}],
"max_runs_per_window": 2,
"window_seconds": 3600,
"circuit_breaker": {"failure_threshold": 3, "window_seconds": 300}
}
- Tell the team when it finally trips the breaker:
{
"name": "payments-breaker-alert",
"trigger_type": "event",
"event_type": "task-failed",
"conditions": [{"field": "name", "op": "eq", "value": "payments.charge"}],
"actions": [{
"type": "slack_webhook",
"webhook_url": "https://hooks.slack.com/...",
"text": "payments.charge is failing repeatedly",
"fields": {"Task": "{event.name}", "Error": "{event.exception}"}
}],
"cooldown_seconds": 300
}
Legacy env-var alerts¶
The ALERT_* env vars are deprecated. On consumer startup they seed the
equivalent built-in automations (alert-task-failed, alert-slow-task,
alert-worker-offline, alert-worker-offline-sweep), which are then managed
like any other automation. Prefer defining automations directly.