# Automation steps

> Reference for every automation step, from send email, wait and condition to audience, contact and suppression steps, plus branches and step failures.

Steps are what a run does after its trigger fires. This page lists every step by context with its settings and rules, explains how steps connect into branches, and shows what happens when a step fails.

## Add and configure steps

On the automation's **Editor** tab (available while the automation is a draft or paused):

- **Add a step:** select the plus button below the last step, or below **Yes** or **No** on a condition. Search the picker or pick from **Actions**.
- **Configure a step:** select it on the canvas. Its settings open in a side panel. Once the step has run, a **Stats** tab appears next to **Configure**.
- **Remove a step:** select it and select **Delete step** in the side panel.
- **Save:** select **Save**. Steps with problems are highlighted with an error count.

## Steps by context

| Step | API key | Contact | Email | Event (API) |
| --- | --- | --- | --- | --- |
| [Send email](#send-email) | `send_email` | Yes | Yes | Yes |
| [Wait / Delay](#wait--delay) | `wait` | Yes | Yes | Yes |
| [Condition (If/Else)](#condition-ifelse) | `condition` | Yes | Yes | Yes |
| [Add to audience](#add-to-audience) | `add_to_audience` | Yes | | |
| [Remove from audience](#remove-from-audience) | `remove_from_audience` | Yes | | |
| [Edit contact](#edit-contact) | `edit_contact` | Yes | | |
| [Forward email](#forward-email) | `forward_email` | | Yes | Yes |
| [Add to suppressions](#add-to-suppressions) | `add_to_suppressions` | | Yes | Yes |
| [Remove from suppressions](#remove-from-suppressions) | `remove_from_suppressions` | | Yes | Yes |
| [Create contact](#create-contact) | `create_contact` | | Yes | Yes |
| [API-only steps](#api-only-steps) | `call_webhook`, `run_automation`, `experiment`, `end` | API | API | API |

Step settings can include placeholders such as `{{contact.first_name}}` or `{{payload.object.to}}`, filled in for each run. See [Data available to steps](/docs/automations/triggers/#data-available-to-steps).

## Send email

Sends an email built from one of your [templates](/docs/templates/).

| Setting | Required | Notes |
| --- | --- | --- |
| **Email template** | Yes | The template to send. With the API, `template_id` takes a `tem_` ID or an alias, which uses the published version. |
| **To (recipient)** | Email and Event contexts | The address to send to. Contact automations always send to the run's contact. |
| **Subject** | No | Overrides the template's subject. |
| **From** | No | Overrides the template's sender, for example `"Acme" <hello@acme.com>`. |
| **Reply to** | No | Overrides the template's reply-to address. |

Rules:

- **The template is rendered with [Temple](/docs/templates/temple/).** In Contact automations, the contact's fields are available directly, so `{{first_name}}`, `{{email}}` and `{{custom_fields.plan}}` work. Every context also has `{{contact.*}}`, `{{payload.*}}` and `{{meta.*}}`. Campaign merge tags such as `{{cf.plan}}` and `{{unsubscribe_url}}` aren't filled in.
- **The sender must be on a verified domain.** The **From** override or the template's sender must use a domain verified in the workspace. **Save** checks overrides, and the step fails at run time if the domain isn't verified.
- **A subject and content are required**, from the template or the overrides.
- **Each email costs 1 credit**, on top of the 3 credits for the run.
- **It doesn't check subscriptions.** The email is sent even if the contact unsubscribed from your audiences. Suppressed addresses are still blocked at delivery, and sandbox workspaces can only send to members' account addresses.
- **No unsubscribe link or loads and clicks.** Automation emails don't get `List-Unsubscribe` headers, and loads and clicks aren't tracked.

The step's **Stats** follow each email it sent through **Accepted** and **Delivered**, and count bounces, failures and complaints.

## Wait / Delay

Pauses the run before the next step.

| Setting | Notes |
| --- | --- |
| **Duration** and **Unit** | **Minutes**, **Hours** or **Days**. The longest wait is 30 days. With the API, set `seconds`, up to `2592000`. |

While a run waits, it stays **Running** and the wait step shows as waiting. Pausing the automation doesn't interrupt waiting runs.

## Condition (If/Else)

Sends the run down the **Yes** branch when the rules match, or the **No** branch when they don't.

- **Rules:** a field, an operator and a value, combined with **All rules match** or **Any rule matches**. The operators are the same as for [trigger filters](/docs/automations/triggers/#filters).
- **Fields:** in Contact automations, **Email**, **First name**, **Last name** and your custom fields. In Email automations, the fields of the trigger's event. With the API, `field` can be any path, with the prefixes `contact.`, `email.`, `payload.` and `meta.`.
- **Branches:** add the next step under **Yes**, **No** or both. A branch with no step ends the run there.

## Add to audience

Contact context. Subscribes the run's contact to the **Audience** you choose. If the contact had unsubscribed from it, it's subscribed again. The step fails if the audience has reached its [subscriber limit](/docs/audiences/#limits). It doesn't start **Added to audience** automations.

## Remove from audience

Contact context. Turns the contact's subscription to the **Audience** off. The contact stays on the audience as an unsubscribed subscriber, unlike **Remove from audience** in the dashboard, which deletes the membership.

## Edit contact

Contact context. Sets one or more fields on the run's contact. Add a row per field, with a **Field name** and a **Value**:

| Field name | Effect |
| --- | --- |
| `first_name`, `last_name`, `email` | Updates that field. |
| `unsubscribed` | Sets the contact's marketing status. Use `true` to unsubscribe the contact from all campaigns. Any non-empty value counts as `true`. |
| Any other name | Stores the value in the custom field with that key, keeping the other custom fields. |

Values can use placeholders, for example `{{payload.object.audience.name}}`. The change doesn't send a `contact.updated` event, so it doesn't start **Contact updated** automations or webhooks.

## Forward email

Email and Event contexts. Sends a copy of the run's email, with its original content and attachments, to the address in **Forward to**. The subject becomes "Fwd: " plus the original subject. The email must come from a verified sending domain in the workspace, and each forward costs 1 credit. See [Forward with automations](/docs/inbound/forward-with-automations/).

## Add to suppressions

Email and Event contexts. Adds an address to your [suppression list](/docs/suppressions/) with type `recipient`, which blocks all sending to it, and the **Reason** you enter (default `automation`). In Email automations, the address is the email's recipient. In Event automations, set `email` with the API, for example `{{payload.email}}`. The step sends a `suppression.created` event.

## Remove from suppressions

Email and Event contexts. Removes the address from the suppression list: the email's recipient in Email automations, or `email` set with the API in Event automations.

## Create contact

Email and Event contexts. Creates a contact and, if you pick an **Audience (optional)**, subscribes it there.

- In Email automations, the contact is the email's recipient address. For received emails, that's the address the email was sent to. To create a contact for the sender instead, set `email` to `{{email.mail_from}}` with the API.
- In Event automations, set `email` and optionally `first_name` with the API.
- If the contact already exists, it's kept as it is and only subscribed to the audience.
- The step doesn't start **Added to audience** automations.

## API-only steps

These steps can be added with the API but not in the dashboard editor yet. They work in every context.

| Step | API key | Config | What it does |
| --- | --- | --- | --- |
| Call webhook | `call_webhook` | `url` (required), `method` (default `POST`), `headers`, `body` | Sends an HTTP request with a JSON content type and records the response. A non-2xx response or a timeout is recorded as the step's outcome (`2xx`, `4xx`, `5xx`, `timeout` or `network_error`) and doesn't fail the run. |
| Run automation | `run_automation` | `automation_id` (required) | Starts a run of another running automation with the same payload, then continues. The other run costs its own 3 credits. Skipped if that automation isn't running. |
| Random split | `experiment` | `variants`: list of `{ "key", "weight" }`, optional `control` | Sends each run down one branch at random, in proportion to the weights. Each variant's branch is named after its `key`. When a variant's branch ends, the run continues on the step's `default` branch. |
| End | `end` | None | Marks the end of a branch. |

```json
{
  "key": "notify-crm",
  "type": "action",
  "action": "call_webhook",
  "config": {
    "url": "https://crm.acme.com/hooks/new-subscriber",
    "method": "POST",
    "headers": { "Authorization": "Bearer crm_token" },
    "body": { "email": "{{contact.email}}", "source": "emailit" }
  }
}
```

## Branches and connections

With the API, an automation is a list of `steps`, each with a unique `key`, and a list of `connections` from one step key to the next:

```json
{
  "steps": [
    { "key": "trigger-1", "type": "trigger", "trigger": "contact.added_to_audience", "config": {} },
    { "key": "is-pro", "type": "action", "action": "condition", "config": {
      "filter": { "match": "all", "rules": [{ "field": "custom_fields.plan", "operator": "equals", "value": "pro" }] }
    } },
    { "key": "send-pro", "type": "action", "action": "send_email", "config": { "type": "template", "template_id": "welcome-pro" } },
    { "key": "send-free", "type": "action", "action": "send_email", "config": { "type": "template", "template_id": "welcome-free" } }
  ],
  "connections": [
    { "from": "trigger-1", "to": "is-pro", "branch": "default" },
    { "from": "is-pro", "to": "send-pro", "branch": "yes" },
    { "from": "is-pro", "to": "send-free", "branch": "no" }
  ]
}
```

- **`branch`** is `default` for a normal next step, `yes` or `no` after a condition, and a variant key after a random split.
- **A condition only follows the branch it picked.** A `default` connection out of a condition is never followed.
- **A step with several outgoing connections on the same branch** starts all of them, and the branches run side by side.
- **Every step must be reachable from a trigger.** In Contact and Email automations with several triggers, all triggers must connect to the same first step.
- **Send `steps` and `connections` together** when you update an automation. Steps whose keys don't change keep their history and stats.

## When a step fails

By default, a failed step fails the whole run, and the remaining steps don't run. The error is saved on the step, and you can read it in the run's details on the **Runs** tab. Set `on_step_failure` to `skip` in the automation's `settings` with the API to let runs continue past failed steps instead.

| Error | Cause |
| --- | --- |
| `send_email: Template '…' not found or not published` | The template was deleted, or the alias has no published version. |
| `send_email: Sending domain is not verified or not found` | The sender's domain isn't verified in the workspace. |
| `send_email: Unable to determine recipient address` | **To (recipient)** is empty, or the contact no longer exists. |
| `send_email: Insufficient credits (…)` | The workspace ran out of credits. |
| `Pro includes 50,000 subscribers per audience.` (or your plan's limit) | An **Add to audience** or **Create contact** step hit a full audience. |
| `forward_email: Source email has no raw content to forward` | The email's content was already removed by your [data retention](/docs/data-retention/) settings. |

See [Runs and stats](/docs/automations/runs/#debug-a-failed-run) for how to find and fix failed runs.

## Related

  - [Triggers](/docs/automations/triggers/): What starts a run.
  - [Recipes](/docs/automations/recipes/): Ready-made automations to start from.

---
Source: https://emailit.com/docs/automations/steps/
