Skip to main content
The Automations engine turns events into actions. Each rule has a trigger (one of 15 event types), zero or more filters / payload-match conditions, and one or more ordered actions. Rules are tenant-scoped, run on Celery, and every run is logged for replay + audit.
Use dispatch(trigger_key, payload) from any backend code to fire a trigger. The engine matches it against user-defined rules and enqueues the action chain — your code doesn’t need to know which rules exist.

Building a rule

In the UI, an automation rule has four sections:
  1. Trigger — pick from 15 trigger types. The form re-renders to show the trigger’s config schema (e.g., note.status_changed shows “from status” / “to status” pickers; schedule.weekly shows day-of-week selectors).
  2. Filters — additional payload-match conditions. The trigger may already filter (e.g., note.created can pre-filter by category); filters here are arbitrary expressions on the payload.
  3. Actions — one or more, ordered. Each action has its own config schema. Actions can reference any field on the trigger payload via Jinja-style templates: {{ payload.note_id }}.
  4. Settings — rate limit (e.g., max once per hour per client_id), dedup key, enabled/disabled, owner.
Saving the rule writes an Automation row plus (for schedule triggers) a corresponding django_celery_beat.PeriodicTask so the schedule registers with the beat.

Trigger reference

Each registers a PeriodicTask row in django-celery-beat. Cancelling the rule deletes the periodic task too.

Action reference

Run history

Every dispatch produces an AutomationRun row: The Run History view (/automations/history) is server-side paginated, filterable by trigger / action / status / date range / client. Click a row → see the original payload and per-step logs.

Replay

POST /api/automations/runs/<run_id>/replay/ re-dispatches the original payload through the rule’s current configuration. Useful when a rule’s action set changes and you want to retroactively apply the new chain. The replayed run is tagged with replayed_from=<original_run_id> for audit.

Cancel

POST /api/automations/runs/<run_id>/cancel/ revokes any queued actions in the chain. In-flight actions complete; downstream actions are skipped. The run finishes with status='cancelled'.

Slack alerts on failure

Pattern: chain agent.run_failedsend_slack:
Same pattern works for automation.run_failed (which is implicit — failed automation runs auto-emit agent.run_failed if a run_agent action was the failing step).

Idempotency + dedup

Each rule has an optional dedup_key template. The engine hashes the rendered key + the rule id and skips dispatch if a run with that hash exists in the last 30 minutes. Useful for noisy triggers — for example, note.status_changed can fire many times during bulk imports.

Rate limiting

Per-rule rate limits (e.g., “max 5 dispatches per minute per client_id”) are stored on the rule and enforced in automations.engine.dispatch before queuing. Slack actions additionally honor a per-workspace token-bucket so we don’t get rate-limited by Slack itself.

Where the code lives