> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usenexio.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Automations

> Learn what Nexio can run for your organization when an event arrives or a schedule comes due, and the guarantees that keep it safe.

An automation lets the platform act on its own: rerun an engine when its data refreshes, start an evaluation when you deploy new code, or tell your systems when a dataset changes. On the platform each automation is a **subscription**: a versioned rule with a trigger (which events or which schedule), an optional condition, and one **effect** (what to do). Each time a subscription matches an event, Nexio records a **dispatch** with a stable ID and runs the effect under that ID, so a retry does not repeat an engine run or an event. A webhook post can arrive more than once, so your receiver deduplicates on the `X-Nexio-Delivery` header (see [Effects](#effects)).

Subscriptions are a capability Nexio operates. Tell Nexio what you want to happen and when; Nexio writes the subscription and turns it on. There is no customer API or portal page for subscriptions. This page explains what a subscription can do so you can ask for the right one, and what guarantees apply.

## How it works

1. A [platform event](/events/platform-events) is recorded, or a schedule slot comes due and the scheduler records a `schedule.fired` event.
2. About every 5 minutes, while your organization's [event log](/events/platform-events#event-log-modes) is in `shadow` or `live` mode, the dispatcher reads up to 500 events it has not yet evaluated, recorded in the last 24 hours and at least 30 seconds ago (so a slow commit is not missed), and matches each against your organization's enabled subscriptions.
3. Each match goes through admission: loop check, shadow check, debounce, and budgets. The result is recorded as a dispatch, whether or not it runs.
4. An admitted dispatch is queued. A worker runs the effect, with retries on transient failure.
5. The dispatch records the outcome. Nexio operators can replay a dispatch or a whole subscription.

Allow about 5.5 minutes between an event and its dispatch: up to 5 minutes until the next pass, plus the 30 second safety window. A backlog of more than 500 events takes more than one pass. It is not a low-latency channel. For run results, use [webhooks](/api-reference/webhooks/overview).

## Triggers

A subscription has one trigger kind.

**Event trigger.** Fires on events that match all of these:

| Rule                         | How it matches                                                                                                                                        |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event_type` (required)      | An exact type such as `dataset.refreshed`, or a family wildcard such as `dataset.*`.                                                                  |
| `subject_prefix` (optional)  | The event's `subject` starts with this text, for example `dataset/served_`.                                                                           |
| `body_match` (optional)      | Every top-level key given must be present in the event body with an equal value. Nested values must be equal as a whole.                              |
| `fires_on_causes` (optional) | The event's `transition_cause` is in this list. The default is `world_change` only, so a backfill or a re-computation does not fire the subscription. |

**Schedule trigger.** Fires on a repeating slot. There are no cron expressions.

| Setting                      | Meaning                                                                                                                                                                                                                                     |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `schedule_interval_hours`    | Hours between slots. Must be greater than 0.                                                                                                                                                                                                |
| `schedule_anchor_utc_minute` | Optional. Minute of the UTC day that slots align to, from 0 to 1439. For example `120` with a 24 hour interval fires at 02:00 UTC every day. Without an anchor, slots align to multiples of the interval counted from 1970-01-01 00:00 UTC. |
| `schedule_min_gap_minutes`   | Minimum minutes between two firings. Default 60.                                                                                                                                                                                            |

Each due slot records one `schedule.fired` event with the slot start as its dedupe key, so a slot fires once even if the scheduler runs twice. The scheduler runs about every 5 minutes.

## Effects

Each subscription has one effect. The effect kinds are a fixed set of four, defined in code. An effect's configuration is checked strictly when Nexio saves the subscription; an unknown field is refused.

| Effect            | What it does                                                                                                                                                                                                  | Configuration                                                                                                                                                                                                                                                                                                                  |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `run_engine`      | Submits an engine run through the same admission checks as `POST /api/v1/engines/{engine_slug}/runs`, including request limits and your organization's run cap. The per-minute API rate limit does not apply. | `engine_slug` (required), `environment_slug`, `engine_version`, `input` (static run input), `input_from_body` (merge the event body into the input; body keys win), `traffic_class`                                                                                                                                            |
| `run_eval_set`    | Starts an [evaluation](/engines/evaluation) of an engine against a frozen evaluation set.                                                                                                                     | `eval_set_id`, or `eval_set_from_body` to read `eval_set_id` from the event body; `candidate_config_hash` to evaluate a specific configuration (default: the engine's active one)                                                                                                                                              |
| `emit_event`      | Records a new platform event caused by the matched one, with `produced_by` set to `rule` and `caused_by` set to the matched event. Use it to chain steps.                                                     | `type` (required; not `schedule.fired`), `subject` (default: the matched event's subject), `body` (static keys; `source_event_id` is always added), `body_from_event` (copy the matched body first; static keys win), `transition_cause` (default: the matched event's cause, so a backfill never turns into a `world_change`) |
| `deliver_webhook` | Signs and posts the event to one of your registered [webhook endpoints](/api-reference/webhooks/overview#platform-event-deliveries-to-the-same-endpoint).                                                     | `endpoint_id` and `environment` (required), `event_type` (overrides the `X-Nexio-Event` header), `extra` (static values delivered under `config` in the body)                                                                                                                                                                  |

A retried dispatch keeps its dispatch ID, and each effect uses that ID so a retry does not repeat its work. The one exception is `deliver_webhook`: a post can reach your receiver and then be sent again when the response is lost, so your receiver deduplicates.

* `run_engine` uses the dispatch ID as the run's idempotency key, so a retry finds the run it already submitted. If admission refuses the run (for example the run cap or a request limit), the dispatch fails permanently with the refusal code.
* `emit_event` uses the dispatch ID as the child event's dedupe key.
* `run_eval_set` stamps the evaluation run with the dispatch, so a retry finds it.
* `deliver_webhook` can post more than once for one dispatch. Every attempt carries the dispatch ID as `X-Nexio-Delivery`; deduplicate on it.

A common chain: an inbound `code.deployed` event matches a subscription (debounced on `commit_sha`) that emits `eval.run.requested`, and a second subscription on `eval.run.requested` runs `run_eval_set`. Repeated deploy events for one commit inside the debounce window start one evaluation.

## Dispatch states

Every match is recorded as a dispatch, including matches that do not run. This is the only place the model is defined.

| Status           | Meaning                                                                                                                                                                                                                      | What happens next                                  |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
| `loop_refused`   | Refused because the event has 4 or more ancestors on its `caused_by` chain, or because this subscription already emitted one of the event's ancestors.                                                                       | Final, unless replayed.                            |
| `skipped_shadow` | The organization's event log is in `shadow` mode. The match is recorded so Nexio can check the subscription before it acts.                                                                                                  | Final, unless replayed.                            |
| `debounced`      | The same debounce key was dispatched inside the debounce window.                                                                                                                                                             | Final, unless replayed.                            |
| `budget_refused` | The hourly or daily budget is used up.                                                                                                                                                                                       | Final, unless replayed.                            |
| `queued`         | Admitted and waiting for a worker.                                                                                                                                                                                           | Moves to `running`.                                |
| `running`        | A worker is running the effect.                                                                                                                                                                                              | Moves to `succeeded` or `failed`.                  |
| `succeeded`      | The effect finished.                                                                                                                                                                                                         | Final, unless replayed.                            |
| `failed`         | The attempt failed. After a transient failure with attempts left, the dispatch waits and runs again. When the last attempt fails, or on a permanent failure, the dispatch is exhausted (the dispatch form of a dead letter). | Retried, or final when exhausted, unless replayed. |

The four refused and skipped states run only if replayed, and do not count toward budgets or debounce windows.

## Budgets and debounce

| Setting                   | Default | Meaning                                                                                                                                                                                                                                                                  |
| ------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `max_dispatches_per_hour` | 60      | Admitted dispatches in the last 60 minutes. The next match is `budget_refused`.                                                                                                                                                                                          |
| `max_dispatches_per_day`  | None    | Admitted dispatches since midnight UTC. Checked after the hourly budget.                                                                                                                                                                                                 |
| `debounce_window_minutes` | 0 (off) | When above 0, a match whose debounce key was dispatched inside the window is `debounced`.                                                                                                                                                                                |
| `debounce_key_path`       | None    | Dot path into the event body, such as `commit_sha` or `account.id`, whose value is the debounce key. Objects only; no array indexes. A missing or null value counts as the empty key. Without a path, the whole subscription shares one key. Requires a positive window. |

## Loop refusal

An automation cannot loop forever. When a matched event has a `caused_by` parent (for example an event recorded by `emit_event`), the dispatcher walks its `caused_by` chain first:

* If the event has 4 or more ancestors on the chain, the match is `loop_refused`.
* If this same subscription emitted any ancestor of the event, the match is `loop_refused` (re-entry).

Both checks run before anything is queued, and the refusal is recorded with the reason.

## Retries and automatic disable

| Setting                    | Default                                  | Meaning                                                                               |
| -------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------- |
| `max_attempts`             | 5                                        | Attempts per dispatch.                                                                |
| Retry wait                 | 30 seconds, doubling, at most 10 minutes | Wait between attempts after a transient failure.                                      |
| `max_consecutive_failures` | 5                                        | Exhausted dispatches in a row before Nexio disables the subscription and records why. |

Failures are either transient (retried) or permanent (not retried). An unclassified error is transient. For `deliver_webhook`, a `2xx` is success; `408`, `429`, and any status of `500` or greater (including nonstandard codes above `599`) are transient; every other code is permanent. This differs from run webhook endpoints, whose retry policy decides; see [Retry policies](/api-reference/webhooks/overview#retry-policies).

## Replay

Nexio operators can replay work, with a named reason that is recorded:

* **Replay one dispatch.** The dispatch is queued again under a fresh claim. It is refused while an attempt is still running.
* **Replay a subscription from a date.** Nexio scans every event since that date and creates a dispatch, marked `is_replay`, for each matching event that has no dispatch for the subscription's current version yet. Replay matches regardless of transition cause. It follows the organization's mode, so in `shadow` it records `skipped_shadow`. It is refused when the event log is `off`.

The dispatcher looks back 24 hours for new events. An event older than that is dispatched only by a replay.

## Versions and audit

Every change to a subscription, including an automatic disable, is saved as a new version with who made it and the reason. A dispatch keeps the version it matched and runs against that version, even if the subscription changes before the dispatch runs. New subscriptions are disabled by default.

## Limits and defaults

| Item                                            | Value                             |
| ----------------------------------------------- | --------------------------------- |
| Dispatcher pass                                 | About every 5 minutes             |
| Minimum event age before evaluation             | 30 seconds                        |
| Look-back for new events                        | 24 hours                          |
| Events evaluated per organization per pass      | 500                               |
| `caused_by` ancestors that refuse a match       | 4 or more                         |
| Attempts per dispatch                           | 5 (default)                       |
| Retry wait                                      | 30 seconds doubling to 10 minutes |
| Hourly budget                                   | 60 dispatches (default)           |
| Consecutive exhausted dispatches before disable | 5 (default)                       |

## What you configure

Nothing directly. Tell Nexio the trigger you want (an event type or a schedule), the condition, and the effect. Nexio sets up the subscription and enables it. If the effect is `deliver_webhook`, register the endpoint first with [Create webhook endpoint](/api-reference/webhooks/create-webhook) and send Nexio its ID.

<CardGroup cols={2}>
  <Card title="Platform events" href="/events/platform-events">
    The events a subscription can match.
  </Card>

  <Card title="Inbound events" href="/events/inbound">
    Send your own events to trigger automations.
  </Card>
</CardGroup>
