> For the complete documentation index, see [llms.txt](https://docs.ilert.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ilert.com/alerting/configure-alerting/event-flows.md).

# Event flows

A routing layer in front of your alert sources: branch on the payload, wait for business hours, rewrite the event, and send it to the source that suits it.

An alert source decides what an event means. An event flow decides **which alert source gets it** — and that turns out to be the thing you need once one endpoint has to serve several teams, several severities, or different behavior during the night.

Your tool sends to the flow instead of to a source. The flow evaluates the payload, follows the first branch that matches, and routes the event onward.

{% hint style="info" %}
**Event flows require the** [**Scale plan**](https://www.ilert.com/pricing) **or higher, and are in beta.** Every node, **Transform event** included, is available on every account that has event flows.
{% endhint %}

## When to reach for one

You do not need an event flow to control noise — the [alert source](/alerting/configure-alerting/alert-sources.md) does that, with filters, grouping and priority. Reach for a flow when the *destination* has to change, or the event has to be fixed before it gets there:

* One Prometheus endpoint, but database alerts should page the database team and everything else the platform team.
* Anything arriving outside business hours should go to a low-priority source instead of the on-call one.
* An event should be held for 10 minutes in case it resolves itself, and only then routed.
* A tool reports its severity as a word in a custom field, and you want that word to set the alert's priority.

If the routing decision is always the same, put the logic on the alert source and skip the flow.

## Build a flow

{% stepper %}
{% step %}

### Create it

In the sidebar, go to **Alerting** → **Event flows** and click **Create new event flow**. Give it a name, and optionally assign [teams](/users-and-access-management/teams.md).
{% endstep %}

{% step %}

### Say what triggers it

Click the **Incoming event** node and, under **How would you like to send events to this flow?**, pick one or more integrations. ilert creates an integration key for each one as soon as you pick it. Point your tool at that key instead of at an alert source's: for an integration with its own URL, you change the URL; for the API integration, the endpoint stays `https://api.ilert.com/api/events` and only the key changes; an email integration gets an address of its own.
{% endstep %}

{% step %}

### Add logic

Click a **+** on the canvas and choose a node. Most flows are a **Branch** or a **Support hours** node followed by a **Route to alert source** on each path.
{% endstep %}

{% step %}

### Save

Click **Save**. The flow is live immediately — there is no separate switch — and starts processing events from the integrations you attached.
{% endstep %}
{% endstepper %}

<figure><img src="https://3394882078-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M76ygPnS4HUcFSX8ulm%2Fuploads%2Fgit-blob-2d26fe515eb2c905bd30915d6610cab1de6a8ec6%2Fevent-flow-canvas.png?alt=media" alt="A saved ilert event flow named Acme Checkout normalization: an Incoming event node reading 1 integration, a Transform event node reading 4 operations, a Route to alert source node naming Acme Checkout API, and an End of flow node. The header carries Edit, View event flow logs and Export to Terraform buttons."><figcaption><p>Each node's second line says what it does: how many integrations feed the flow, how many rules the transform has, which alert source receives the event.</p></figcaption></figure>

## The nodes

| Node                      | Does                                                                                                     | Branches it adds                                                                                                     |
| ------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| **Branch**                | Sends the event down the first branch whose conditions match                                             | one per branch you add, up to 8, plus `Else`. The editor lets you add more, but ilert rejects the node when you save |
| **Route to alert source** | Sends the event to an alert source, optionally overriding its escalation policy or notification priority | none                                                                                                                 |
| **Support hours**         | Splits on your [support hours](/alerting/configure-alerting/support-hours.md) definition                 | `In hours`, `Out of hours`                                                                                           |
| **Wait**                  | Holds the event for a duration, or until support hours start or end                                      | none                                                                                                                 |
| **Transform event**       | Rewrites the event's fields before the next node runs                                                    | none                                                                                                                 |

Every path ends at **End of flow**. An event that reaches it without passing a **Route to alert source** node creates no alert — which is a legitimate way to drop events, and also the most common reason a flow "does nothing". The run is still recorded, though: the event appears in the [event flow logs](#see-what-a-flow-actually-did), with the path it took ending in **Flow ended**.

The flow continues after **Route to alert source**, so a second **Route to alert source** below it sends the event to a second alert source. Overrides carry over: if the first node overrode the escalation policy or the priority, the second source gets those values too, unless its own node overrides them.

### Branch

Click **Add branch** for each path you need, and give each its conditions. Conditions are built the same way as an [event filter](/alerting/configure-alerting/alert-sources.md#event-filter): filter groups combined with AND / OR. They can test the integration type, the event timestamp, the event's own fields (`context.event.summary`, a label as `context.event.labels.<key>`), and the custom fields found in your integrations' sample payloads (`context.event.customDetails.<path>`). **Event timestamp** **is during** or **is not during** a support hour or an on-call schedule branches on when the event arrived. The code editor takes [ICL](/developer-docs/icl-ilert-condition-language.md) directly.

Branches are evaluated **in order, first match wins** — so order matters, and a broad condition placed early will shadow the specific ones after it. Anything matching nothing takes the `Else` path. A condition that fails to evaluate counts as not matching, and the next branch is tried.

{% embed url="<https://www.youtube.com/watch?v=BJ8OEErGiDo>" %}
Branch order: the first match wins, and Else takes the rest
{% endembed %}

Two conditions do not match what they seem to:

* **The alert key is `context.event.incidentKey` in a condition.** The editor offers `alertKey`, but a condition on `context.event.alertKey` matches no event. Write `context.event.incidentKey` in the code editor instead.
* **The "...by event type" shortcut** adds branches named `PENDING`, `ACCEPT` and `RESOLVE`. An event that opens an alert has the type `ALERT`, so it matches none of them and takes `Else`. Change the first branch's condition to `ALERT`, or treat `Else` as the branch for new alerts.

### Wait

Two modes, and the second is the interesting one:

* **Duration** — hold for a number of seconds, minutes, hours or days, from 10 seconds up to 14 days.
* **Support hours** — **Wait until start of support hours** or **Wait until end of support hours**, measured against a support hour you pick. An event that is already past the boundary it would wait for continues immediately: the start option holds nothing that arrives in hours, and the end option holds nothing that arrives out of hours. A boundary that never comes is the same case: against support hours that cover 24/7 with no out-of-hours holiday exception ahead, **Wait until end of support hours** has no end to wait for, so the event continues immediately.

Waiting until business hours begin is how you stop a low-severity event from paging at 3am without dropping it: it arrives at 9.

A waiting event is tied to its alert key, and that is what makes "hold 10 minutes in case it resolves" work:

* **An accept or resolve event ends the wait.** When an `ACCEPT` or `RESOLVE` event with the same alert key reaches a **Wait** node in the same flow, the waiting event ends, and so does the accept or resolve event. Neither reaches an alert source, so nobody is paged for a problem that fixed itself.
* **A repeat is dropped.** Another `ALERT` event with the same alert key that reaches a **Wait** node while the first one waits ends there, the way ilert groups events into an open alert.
* **The accept or resolve event has to pass through a Wait node.** If a branch sends it around the wait, it is routed at once, and the waiting event still creates its alert later. An accept or resolve event that finds nothing waiting is held like any other event; one that is past a support-hours boundary is held for 10 seconds. While it is held, a new `ALERT` event with the same alert key that reaches a **Wait** node is dropped as a repeat.
* **Events without an alert key** are never grouped or ended this way.

If more than 1,000 events in your account are waiting at the same time, further events end at the **Wait** node instead of being routed, and the log gives the end reason `LIMITED`.

### Transform event

Rewrites the event before the next node runs: set a value, copy one field into another, map values onto new ones, compose text from a template, merge properties, or append to a list. Later nodes, and the alert source the event is routed to, see the rewritten event. See [Transform event](/alerting/configure-alerting/event-flows/transform-event.md).

## Working with large flows

The three-dot menu on every node except **Incoming event** and **End of flow** offers **Settings**, **Copy node**, **Copy subtree**, **Cut subtree** and **Delete subtree**. A subtree is a node plus everything below it, so a branch you have tuned can be duplicated in one action. Paste from a **+**: **Paste node** anywhere, **Paste subtree** on an empty path, or **Replace subtree** on a connection, which replaces everything below it after you confirm. The clipboard lasts only while the editor is open, so you cannot paste into another flow.

A flow can hold up to 300 nodes, nested up to 75 levels deep.

**Export to Terraform**, in the header of a saved flow, produces the flow as Terraform configuration — useful when a flow is something you would rather review in a pull request than rebuild by hand in another account.

## See what a flow actually did

**Alerting** → **Event flows** → **Event flow logs**, or **View event flow logs** on a single flow. Select an entry to open **Event flow log details**: one entry for each node the event passed. A **Branch** names the branch it continued with, a **Route to alert source** names the source, a **Transform event** lists the fields it changed, and a **Wait** says how long the event was paused, when it resumed, or — under **Ended by wait** — the **End reason**: `ACCEPTED`, `RESOLVED`, `GROUPED` or `LIMITED`.

Start here when a flow did not do what you expected. It reports what happened, rather than what the canvas suggests should have.

## FAQ

### Can one integration feed several flows?

Not the same integration key. Each integration you pick on an **Incoming event** node gets its own key, which feeds that flow only. To feed a second flow, pick the same integration type on the second flow's **Incoming event** node and send to the new key.

### What happens if no branch matches?

The event follows the `Else` path. If that path has no **Route to alert source** node, no alert is created.

### Do event flows replace alert sources?

No — they sit in front of them. A flow's job is to choose a destination and, if needed, fix the event on the way. The alert source still owns filtering, grouping, templates and escalation once the event lands, with two exceptions. Any priority the event carries when it leaves the flow, whether your tool sent it, a **Transform event** node set it or a **Route to alert source** node overrode it, replaces the alert source's priority setting. And a **Route to alert source** node can override the escalation policy.

### Can I test a flow before pointing production traffic at it?

There is no dry-run mode. Send test events to the flow's integration key and read the event flow logs. A path that reaches **Route to alert source** creates a real alert and pages whoever is on call, so test with a copy of the flow whose paths end without one: the logs still show every node the event passed.

### Where do I configure support hours?

**Alerting** → **Support hours**. See [Support hours](/alerting/configure-alerting/support-hours.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.ilert.com/alerting/configure-alerting/event-flows.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
