# Send emails automatically with automations

> Start a series of waits, emails, conditions and actions when a contact joins a list, gets a tag, fills in a form or orders, and read what each step did.

Source: https://www.coritan.com/docs/organizations/commerce/email-automations/

In the dashboard:

- /dashboard/organizations/…/commerce/automations: https://www.coritan.com/dashboard/organizations

**Automations** holds the emails and actions that start when a contact does something, such as joining a list, getting a tag, filling in a form or placing an order. An automation is a *trigger* and a flow of steps. When the trigger happens to a contact, that contact goes through the steps one at a time. Once an automation is on, it runs for each contact on its own.

## Before you begin

- You need Tier 1 support or higher to read automations and Tier 3 support or higher to change them.
- Set up the sending domain ([Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/)). You can build and save an automation before that, but its emails cannot go out, and a test email needs it.
- Make what the trigger and the steps name first: the list, the form, the tag or the date field, and the saved designs.
- An automation reaches only subscribed contacts. [Manage your email contacts, lists and segments](/docs/organizations/commerce/email-contacts/) covers how contacts become subscribed.

## The store's flows and automations

The store's own emails for a cart left behind, a checkout that went unpaid and an order to reorder stay under [Flows](/docs/organizations/commerce/marketing/). They are three fixed emails with their own timing, and they keep working as they are. Automations are for everything that starts from your audience: a welcome series, a thank you after a first order, a win-back for quiet customers, a reminder before a date.

Flows and automations share one suppression list. An address that unsubscribes from either gets nothing from the other. An address can get both a flow email and an automation email, so avoid two series that say the same thing to the same shopper.

## Make an automation

1. In the [dashboard](https://www.coritan.com/dashboard/organizations), open the organization, then **Commerce**, then **Email**, then **Automations**.
2. Select **New automation…**.
3. Enter a **Name**. Only you see it.
4. Choose **Starts when**, and fill in what the trigger needs.
5. Choose **How often a contact can start**: `Once per contact` or `Every time`.
6. Select **Create automation**. The builder opens on a draft.

Add steps from **Add first step** and **Add step after**. A step opens in its own card when you select **Edit**. Each card has an actions menu (**Actions for step**) with **Move up**, **Move down** and **Delete step…**. Select **Save** to keep your changes, then **Turn on**.

An automation is **Draft** until you turn it on, **On** while it runs, **Paused** while stopped, and **Archived** when you are done with it. The list filters by these, with **All**.

### Triggers

| Trigger | When a contact starts |
| --- | --- |
| `Joins a list` | A subscribed contact joins the list you choose, from a form, an import or a person adding them. |
| `Gets a tag` | The tag you name is added to a contact. |
| `Fills in a form` | Someone signs up through the form you choose. With a confirmation email, they start once they confirm. |
| `A date field comes around` | Every year on the day held in a custom date field, such as a renewal date. Set `Days after the date`: a negative number starts before the date, a positive one after it, and 0 on the day. A contact starts once a year, whatever the entry limit says. |
| `Places a first order` | A subscribed contact places the first order they have placed in your store. |
| `Places an order` | A subscribed contact places an order. Set the entry limit to `Every time` to start again after each order. |
| `Buys a product` | A subscribed contact orders the product you choose, by its product ID or a variant's SKU. |
| `Has not ordered for a while` | A subscribed contact's last order is the number of days old you set, from 1 to 730. It checks once a day. |

A contact enters only while subscribed. A contact does not enter an automation twice unless the entry limit is `Every time` and their earlier run is over. An order starts a contact once, however many times the order's event arrives.

### Steps

| Step | What it does |
| --- | --- |
| `Wait` | Holds the contact for a number of **Days**, **Hours** and **Minutes**. Set at least one, up to 365 days in all. |
| `Wait until a time of day` | Holds the contact until the clock next shows the **Time of day**, such as 09:00, in the **Time zone** you choose. UTC is the default. |
| `Send an email` | Queues an email to the contact. It needs a **Subject** of up to 200 characters, and a design or a **Saved design**. **Preview text** and **Sender name** are optional. |
| `If / else` | Sends the contact down a **Yes** path or a **No** path. See [Conditions](#conditions). |
| `Add a tag`, `Remove a tag` | Puts a tag on the contact, or takes it off. A tag is created when it does not exist. |
| `Add to a list`, `Remove from a list` | Adds the contact to a list, or takes it off. A contact who left a list stays off it. |
| `Call a webhook` | Posts the contact and the run to your address as signed JSON. See [Webhook steps](#webhook-steps). |
| `End` | Ends the contact's run here. |

An automation has at most 40 steps, and a series cannot loop back. A step with nothing after it ends the run.

The `Send an email` step opens the designer on its own page: select **Design the email**, then **Save email**. It has the same blocks and merge tags as a campaign ([Write, test and send email campaigns](/docs/organizations/commerce/email-campaigns/#design-the-email)). Choose **Use a saved design instead** to pick a saved design, and **Design the email here instead** to go back. An automation's emails count opens and clicks, and they carry your postal address and an unsubscribe link like every marketing email.

### Conditions {#conditions}

A *condition* is a step that splits the flow in two by a test. Under **What to check**, choose:

- **Opened the last email**: yes when the contact opened the last email this automation sent them, and no when they did not. Opens by mail privacy tools do not count.
- **Matches a rule**: a rule or group of rules built as in a segment, over the contact's details, tags, lists, engagement and orders ([The rule fields](/docs/organizations/commerce/email-contacts/#the-rule-fields)).

Add steps to the **Yes** and **No** paths with **Add step to yes** and **Add step to no**. A condition ends its path unless you add steps to it.

### Webhook steps {#webhook-steps}

A `Call a webhook` step posts to an `https` address on a public host. The address is checked when you save it and again before each request. The body is compact JSON:

```json
{"event": "marketing.automation.step", "run_id": 912, "automation_id": 41, "step_id": "notify",
 "livemode": true, "contact": {"id": 3307, "email": "alex@example.com"}}
```

The request carries `X-Coritan-Signature: t=<unix seconds>,v1=<hex>`. The hex is the HMAC-SHA256, made with your automation secret, of the value of `t`, a full stop, and the body as sent. This is the scheme of [organization webhooks](/docs/organizations/webhooks/#signatures), under another header name. In the step's card, select **Show signing secret** to read the secret. It never changes, and Tier 3 support or higher can read it.

```python
import hashlib, hmac

def verified(secret: str, header: str, body: bytes) -> bool:
    parts = dict(item.split("=", 1) for item in header.split(","))
    expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts.get("v1", ""))
```

Each attempt has a limit of 5 seconds, and there is one retry. A failed call is written to the run's error and the automation goes on to the next step.

## Turn it on

**Turn on** checks the whole flow first. It refuses an automation with no steps, a step that is not valid, a loop, or a list, form or saved design that no longer exists, and it names the step. Contacts who were paused inside the automation go on from where they stopped.

An automation that is **On** does not take changes to its trigger or steps. Select **Pause…** first. You can change its name and its entry limit at any time. **Pause…** stops every run where it is, and nobody new enters. Emails already queued still go out. Turn it on again to continue. While an automation is paused, contacts inside it keep their place when you change its steps.

## Send a test

Open **Automation actions** and choose **Send test…**. Enter an address under **Send to** and select **Send test**. It sends the first email of the automation to that address, with a sample contact's details and `[Test]` in the subject. It starts no run and records no tracking. An address on the suppression list is skipped. A person or key can send 30 tests an hour, and a test needs sending to be ready.

## Read what it did

**Results** shows **Entered**, **Inside now**, **Completed**, **Left early**, **Failed**, **Emails sent**, **Opens**, **Clicks** and **Revenue**. Opens and clicks leave out machine opens ([Machine opens](/docs/organizations/commerce/email-campaigns/#machine-opens)).

Under **Flow**, each step shows how many contacts are waiting there, and an email step shows how many it sent, and how many were opened and clicked.

**Runs** lists the contacts that entered, newest first, with their **Status**, the step they are **At**, the date of their **Next step** and when they **Started**. Filter by **Any status**, **Running**, **Waiting**, **Completed**, **Left early** or **Failed**. A run that ends early shows why: the contact was deleted, is no longer subscribed, or cannot be mailed at an email step, such as an address on the suppression list. A step that fails is tried again after 5 minutes, then after 10, and after three tries the run is **Failed** with the reason.

## Archive or delete an automation

- **Archive automation…** ends every run inside it and skips the emails not yet sent. Its numbers stay. An archived automation cannot be turned on again, so make a new one.
- **Delete automation…** removes an automation that was never turned on. One that has run answers that it has history. Archive it instead.

## Examples to build

Each example uses the triggers and steps above. Names are for a board game shop and a coffee roaster.

Welcome new subscribers
: Trigger `Joins a list` with your newsletter list. Steps: `Send an email` that says thanks and what to expect, `Wait` 3 days, `Send an email` with a product grid of your best sellers.

Thank a first-time buyer
: Trigger `Places a first order`. Steps: `Wait` 7 days, `Send an email` that asks how the first order went, `Add a tag` `first-order`. Use the tag later in a segment.

Win back a quiet customer
: Trigger `Has not ordered for a while` with 90 days. Steps: `Send an email` with a coupon block, `Wait` 7 days, `If / else` on **Opened the last email**. On **Yes**, `Add a tag` `warm`. On **No**, `Send an email` that reminds them the code is about to end.

Remind before a renewal
: A custom date field `renewal_date` on each coffee subscriber. Trigger `A date field comes around` with `Days after the date` set to -7. Step: `Send an email` that reminds them their next bag is on its way.

## Result

- A contact who matches the trigger appears under **Runs** and moves through the steps. Each email it sends counts under **Emails sent**.
- Pausing stops every run where it is, and turning the automation on again lets them continue.

## Troubleshooting

**Turn on** names a step
: That step is not valid, or it names a list, form or design that is gone. Fix the step and select **Turn on** again.

The trigger and steps cannot be changed
: The automation is **On**. Select **Pause…**, change it, then turn it on again.

Nobody enters
: The contact must be subscribed and match the trigger. A contact who entered before does not enter again with `Once per contact`. `Has not ordered for a while` and `A date field comes around` check once a day.

A run is **Left early**
: Open the run's reason. The contact was deleted, unsubscribed, or cannot be mailed, such as an address on the suppression list.

A run is **Failed**
: A step failed three times. The run's reason says why, such as a design that no longer exists.

**Send test…** says sending is not ready
: Set up the sending domain first ([Set up your sending domain and warm-up](/docs/organizations/commerce/email-sending/#when-sending-is-not-ready)).

An automation cannot be deleted
: It has been turned on, so it has history. Archive it.

## Related

- [Set up email marketing for your store](/docs/organizations/commerce/email-marketing/)
- [Manage your email contacts, lists and segments](/docs/organizations/commerce/email-contacts/)
- [Write, test and send email campaigns](/docs/organizations/commerce/email-campaigns/)
- [Win back shoppers with marketing emails](/docs/organizations/commerce/marketing/)

## With the API

The routes are under `https://api.coritan.com/api/v1/orgs/{org_slug}/marketing/automations`. Reading takes `marketing.automations:read` and changing takes `marketing.automations:write` ([API key scopes](/docs/organizations/commerce/email-marketing/#api-key-scopes)).

`GET /automations/catalog` lists every trigger and step with its `label`, its `params` and, for a step, the `branches` it leads out of (`next`, or `yes` and `no`). It also lists the `entry_limits` and `limits.max_steps`.

`POST /automations` makes a draft and answers `201` with `{automation}`:

```bash
curl -X POST "https://api.coritan.com/api/v1/orgs/acme/marketing/automations" \
  -H "X-API-Key: $ORG_API_KEY" -H "Content-Type: application/json" \
  -d '{"name": "Welcome series", "entry_limit": "once",
       "trigger": {"type": "list_joined", "list_id": 3},
       "steps": [
         {"id": "hello", "type": "send_email", "next": "pause", "subject": "Welcome to the shop",
          "template_id": 12},
         {"id": "pause", "type": "wait", "next": "check", "days": 3},
         {"id": "check", "type": "condition", "yes": "tag", "no": "again",
          "rule": {"field": "opened_last_email", "op": "is_true"}},
         {"id": "tag", "type": "tag", "tag": "warm"},
         {"id": "again", "type": "send_email", "subject": "In case you missed it", "template_id": 13}]}'
```

The first step is `steps[0]`. `next` names the step that follows, and a condition names `yes` and `no`. A step with no `next` ends the run. An `id` is 1 to 32 letters, digits, dashes or underscores. The trigger takes the parameters the catalog lists:

| Trigger `type` | Parameters |
| --- | --- |
| `list_joined` | `list_id` |
| `tag_added` | `tag` |
| `form_submitted` | `form_id` |
| `field_date` | `field` (a date field's key) and `offset_days` from -365 to 365 |
| `first_order`, `order_placed` | None. |
| `product_purchased` | One of `product_id` or `sku`. |
| `no_order` | `days`, from 1 to 730 |

| Step `type` | Parameters |
| --- | --- |
| `wait` | `days`, `hours` and `minutes`, at least a minute and at most 365 days in all. |
| `wait_until` | `time` as `09:00`, and an optional `tz` such as `Europe/Berlin`. |
| `send_email` | `subject`, optional `preheader` and `from_name`, and a `design` or a `template_id`. |
| `condition` | `yes`, `no` and a `rule`: a segment rule or group, or `{"field": "opened_last_email", "op": "is_true"}`. |
| `tag`, `untag` | `tag` |
| `add_to_list`, `remove_from_list` | `list_id` |
| `webhook` | `url`, an `https` address on a public host. |
| `exit` | None. |

An automation is `{id, name, status, trigger, steps, entry_limit, activated_at, stats, created_at, updated_at}`. `status` is `draft`, `active`, `paused` or `archived`. `stats` holds `entered`, `active`, `completed`, `exited`, `failed`, `emails_sent`, `opens`, `clicks`, `orders` and `revenue` in minor units. A refused setting answers `422` with `invalid` and a `field` such as `steps.check.rule`.

- `GET /automations` lists automations, newest first, as `{total, limit, offset, automations}`. `status` filters. Archived automations are left out unless you ask for `archived`.
- `GET /automations/{automation_id}` reads one. `PATCH` changes the `name` and `entry_limit` at any time, and the `trigger` and `steps` while the automation is a draft or paused. An active one answers `409` with `automation_active`.
- `POST /automations/{automation_id}/activate` turns it on, `.../pause` pauses it and `.../archive` archives it. Pausing an automation that is not on answers `409` with `automation_not_active`, and turning on an archived one answers `409` with `automation_archived`.
- `DELETE /automations/{automation_id}` answers `{deleted: true, id}`, or `409` with `automation_has_history` once it has run.
- `GET /automations/{automation_id}/runs` lists the runs, newest first. `status` is `active`, `waiting`, `completed`, `exited` or `failed`. A run is `{id, contact_id, status, current_step, next_run_at, error, context, livemode, started_at, completed_at}`, with the contact's `email` when your key or role can also read contacts.
- `GET /automations/{automation_id}/stats` answers `{stats, steps}`, where each step shows `waiting`, `emails_sent`, `opens`, `clicks`, `orders` and `revenue`.
- `POST /automations/{automation_id}/test` takes `{"email": "owner@example.com"}` and answers `{sent, skipped}`. It answers `422` when the automation has no email step, `429` after 30 tests an hour, and `409` with `sending_not_ready` when sending cannot start.
- `GET /automations/webhook-secret` answers `{secret, header, algorithm, signed_payload}`. It takes `marketing.automations:write`.

## API

- `GET /api/v1/orgs/{org_slug}/marketing/automations`: Your automations, newest first, each with its stats (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-automations)
- `POST /api/v1/orgs/{org_slug}/marketing/automations`: Make a draft automation (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-automations)
- `GET /api/v1/orgs/{org_slug}/marketing/automations/catalog`: Every trigger and step an automation can use, with its settings and a short help text (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-automations-catalog)
- `GET /api/v1/orgs/{org_slug}/marketing/automations/webhook-secret`: The secret that signs your automation webhook steps (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-automations-webhook-secret)
- `GET /api/v1/orgs/{org_slug}/marketing/automations/{automation_id}`: One automation with its trigger, steps and stats (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-automations-automation-id)
- `PATCH /api/v1/orgs/{org_slug}/marketing/automations/{automation_id}`: Update automation (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-patch-api-v1-orgs-org-slug-marketing-automations-automation-id)
- `DELETE /api/v1/orgs/{org_slug}/marketing/automations/{automation_id}`: Delete an automation that was never turned on (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-delete-api-v1-orgs-org-slug-marketing-automations-automation-id)
- `POST /api/v1/orgs/{org_slug}/marketing/automations/{automation_id}/activate`: Turn an automation on (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-automations-automation-id-activate)
- `POST /api/v1/orgs/{org_slug}/marketing/automations/{automation_id}/archive`: Archive an automation (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-automations-automation-id-archive)
- `POST /api/v1/orgs/{org_slug}/marketing/automations/{automation_id}/pause`: Pause an automation (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-automations-automation-id-pause)
- `GET /api/v1/orgs/{org_slug}/marketing/automations/{automation_id}/runs`: The contacts that entered the automation, newest first (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-automations-automation-id-runs)
- `GET /api/v1/orgs/{org_slug}/marketing/automations/{automation_id}/stats`: Automation stats (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-get-api-v1-orgs-org-slug-marketing-automations-automation-id-stats)
- `POST /api/v1/orgs/{org_slug}/marketing/automations/{automation_id}/test`: Send test email (https://www.coritan.com/docs/api/reference/organizations/marketing/marketing/#op-post-api-v1-orgs-org-slug-marketing-automations-automation-id-test)
