---
title: "Automations"
description: "Every operation in this group: what it accepts, what it returns and the errors it can answer with."
url: "https://openemail.uk/docs/api/reference/automations"
area: "API"
category: "Reference"
---

# Automations

Every operation in this group: what it accepts, what it returns and the errors it can answer with.

## Operations

Emails and other steps that run by themselves for each contact: a welcome series when somebody joins an audience, a nudge three days after a trial starts, a note on a birthday. An automation has a trigger, which says what puts a contact in, and steps: send an email from a template, wait, branch on what the contact did, add them to or remove them from an audience, write a custom field, call a webhook, or end.

You edit a draft `definition` and publish it. Publishing saves a numbered version and sets the automation `live`, and each contact stays on the version they entered on, so an edit never changes the path of somebody halfway through. Settings apply as soon as they are saved. A plan covers a number of live automations: 1 on Free, 10 on Starter and 50 on Business, with no limit on Enterprise.

Every email it sends goes out on the broadcast lane with a one-click unsubscribe, counts toward the monthly sends and skips suppressed addresses. Publishing, resuming and sending a test also need `emails:send`, because the automation then sends mail for whoever published it. Reading needs `automations:read` and everything else `automations:write`.

### `GET /automations`

List automations

Every automation in the workspace, the most recently saved first, without the definition or settings. `counts` is counted at the moment of the read.

An app a member connected lists only the automations that member made.

Requires the `automations:read` scope.

- Scopes: `automations:read`.

**Query parameters**

- `status` (`string`, one of `"draft"`, `"live"`, `"paused"`, `"archived"`): Only automations in this state.
- `limit` (`integer`, at least 1, at most 100, default `50`): Rows per page, 1 to 100.
- `cursor` (`string`): The id of the last automation on the previous page. Keyset, not offset: pass the previous page's `nextCursor`. One that names nothing in this list is a 400 `invalid_cursor`.

**Returns**

- `200` `AutomationList`: A page of automations.

**Errors**

- The errors every operation can return: `400`, `401`, `403`, `404`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.list()`](https://openemail.uk/docs/sdk/reference/automations#list), [`automations.listAll()`](https://openemail.uk/docs/sdk/reference/automations#listAll), [`automations.iterate()`](https://openemail.uk/docs/sdk/reference/automations#iterate); Python [`automations.list()`](https://openemail.uk/docs/python/reference/automations#list), [`automations.list_all()`](https://openemail.uk/docs/python/reference/automations#listAll), [`automations.iterate()`](https://openemail.uk/docs/python/reference/automations#iterate); Ruby [`automations.list`](https://openemail.uk/docs/ruby/reference/automations#list), [`automations.list_all`](https://openemail.uk/docs/ruby/reference/automations#listAll), [`automations.iterate`](https://openemail.uk/docs/ruby/reference/automations#iterate); PHP [`automations->list`](https://openemail.uk/docs/php/reference/automations#list), [`automations->listAll`](https://openemail.uk/docs/php/reference/automations#listAll), [`automations->iterate`](https://openemail.uk/docs/php/reference/automations#iterate); Go [`Automations.List`](https://openemail.uk/docs/go/reference/automations#list), [`Automations.ListAll`](https://openemail.uk/docs/go/reference/automations#listAll), [`Automations.Iterate`](https://openemail.uk/docs/go/reference/automations#iterate); Java [`automations().list`](https://openemail.uk/docs/java/reference/automations#list), [`automations().listAll`](https://openemail.uk/docs/java/reference/automations#listAll), [`automations().iterate`](https://openemail.uk/docs/java/reference/automations#iterate); C# [`Automations.ListAsync`](https://openemail.uk/docs/csharp/reference/automations#list), [`Automations.ListAllAsync`](https://openemail.uk/docs/csharp/reference/automations#listAll), [`Automations.IterateAsync`](https://openemail.uk/docs/csharp/reference/automations#iterate); CLI [`openemail automations list`](https://openemail.uk/docs/cli/reference/automations#automations-list); MCP [`listAutomations`](https://openemail.uk/docs/mcp/tools/automations#listAutomations).

### `POST /automations`

Create an automation

Makes a draft from `definition`, from a `starter`, or empty when neither is sent, and answers with the whole automation. A draft may be incomplete: `problems` in the answer lists what is still missing, and nothing runs until `POST /automations/{id}/publish`.

Requires the `automations:write` scope.

- Scopes: `automations:write`.

**Request body**

- `name` (`string`, required, 1 to 120 characters): What the workspace calls the automation, trimmed, 1 to 120 characters. Contacts never see it, and it need not be unique.
- `description` (`string`, nullable, up to 500 characters): A note for the workspace, at most 500 characters. Contacts never see it.
- `starter` (`string`, 1 to 64 characters): Begin from a starter, by its `slug`: `welcome-series`, `trial-follow-up`, `birthday` or `win-back`. Ignored when `definition` is sent. A starter leaves the audience, the templates and the from address for you to fill in.
- `definition` (`object`): What starts an automation and what it then does. Paths never join or loop: every step is reached from exactly one place, and a definition holds at most 50 steps.
  - `trigger` (`object`, required, nullable): What puts a contact into the automation. Null in a draft that has none yet, and a draft cannot be published without one.
    - `kind` (`string`, required, one of `"audience_joined"`)
    - `audienceId` (`string`, required, up to 64 characters)
    - `includeImported` (`boolean`, required)
  - `entry` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`): The `key` of the first step, or null while the draft has no steps.
  - `steps` (`object[]`, required, up to 50 items): Every step, in any order. Each has a unique `key`, and names the step after it by key in `next`, or in `yes` and `no` for a branch. Null ends the path there.
    - `key` (`string`, required, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `name` (`string`, up to 80 characters)
    - `kind` (`string`, required, one of `"send_email"`)
    - `next` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `templateId` (`string`, required, up to 64 characters)
    - `templateVersion` (`integer`, required, nullable, at least 1)
    - `from` (`object`, required)
      - `email` (`string`, required, up to 320 characters)
      - `name` (`string`, up to 120 characters)
    - `replyTo` (`string`, required, nullable, up to 320 characters)
    - `subject` (`string`, required, nullable, up to 300 characters)
    - `props` (`Record<string, object>`, required)
- `settings` (`object`): How the automation runs: `timezone`, an IANA zone such as `Europe/London`, `sendWindow`, the days and minutes of the day in which emails may go out or null for any time, `reentryDays`, how many days after finishing a contact may enter again or null for once only, `exitOnLeave`, whether a contact leaves when they leave the audience that started it, and `listAudienceId`, the audience unsubscribes are recorded in. Every field is optional.
  - `timezone` (`string`, 1 to 64 characters)
  - `sendWindow` (`object`, nullable)
    - `days` (`integer[]`, required, up to 7 items)
    - `startMinute` (`integer`, required, at least 0, at most 1440)
    - `endMinute` (`integer`, required, at least 0, at most 1440)
  - `reentryDays` (`integer`, nullable, at least 1, at most 3650)
  - `exitOnLeave` (`boolean`)
  - `listAudienceId` (`string`, nullable, up to 64 characters)

**Returns**

- `201` `Automation`: Created, as a draft.

**Errors**

- `422`: `invalid_parameter` for a value of the wrong shape, `unknown_parameter` for a key the body does not take, `invalid_automation` for an unknown `starter` or a definition that breaks a rule of its structure, such as two steps with one key, or `workspace_limit_reached` when the workspace holds the most automations it may.
- The errors every operation can return: `400`, `401`, `403`, `404`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.create()`](https://openemail.uk/docs/sdk/reference/automations#create); Python [`automations.create()`](https://openemail.uk/docs/python/reference/automations#create); Ruby [`automations.create`](https://openemail.uk/docs/ruby/reference/automations#create); PHP [`automations->create`](https://openemail.uk/docs/php/reference/automations#create); Go [`Automations.Create`](https://openemail.uk/docs/go/reference/automations#create); Java [`automations().create`](https://openemail.uk/docs/java/reference/automations#create); C# [`Automations.CreateAsync`](https://openemail.uk/docs/csharp/reference/automations#create); CLI [`openemail automations create`](https://openemail.uk/docs/cli/reference/automations#automations-create); MCP [`createAutomation`](https://openemail.uk/docs/mcp/tools/automations#createAutomation).

### `GET /automations/starters`

List the automation starters

The starting points the app offers when somebody makes an automation, each with its whole `definition`. Not paginated and not workspace data. The `slug` is what `POST /automations` takes as `starter`. A starter leaves the audience, the templates and the from address empty for you to fill in.

Requires the `automations:read` scope.

- Scopes: `automations:read`.

**Returns**

- `200` `AutomationStarterList`: Every starter.

**Errors**

- The errors every operation can return: `400`, `401`, `403`, `404`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.listStarters()`](https://openemail.uk/docs/sdk/reference/automations#listStarters); Python [`automations.list_starters()`](https://openemail.uk/docs/python/reference/automations#listStarters); Ruby [`automations.list_starters`](https://openemail.uk/docs/ruby/reference/automations#listStarters); PHP [`automations->listStarters`](https://openemail.uk/docs/php/reference/automations#listStarters); Go [`Automations.ListStarters`](https://openemail.uk/docs/go/reference/automations#listStarters); Java [`automations().listStarters`](https://openemail.uk/docs/java/reference/automations#listStarters); C# [`Automations.ListStartersAsync`](https://openemail.uk/docs/csharp/reference/automations#listStarters); CLI [`openemail automations list-starters`](https://openemail.uk/docs/cli/reference/automations#automations-list-starters); MCP [`listAutomationStarters`](https://openemail.uk/docs/mcp/tools/automations#listAutomationStarters).

### `GET /automations/{id}`

Retrieve an automation

The whole automation: the draft `definition`, the `published` definition that is running, `settings`, fresh `counts` and `problems`, what would stop the draft from being published. An automation in another workspace is a 404, never a 403.

Requires the `automations:read` scope.

- Scopes: `automations:read`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Returns**

- `200` `Automation`: The automation.

**Errors**

- `404`: `automation_not_found` when the id names no automation you can reach.
- The errors every operation can return: `400`, `401`, `403`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.get()`](https://openemail.uk/docs/sdk/reference/automations#get); Python [`automations.get()`](https://openemail.uk/docs/python/reference/automations#get); Ruby [`automations.get`](https://openemail.uk/docs/ruby/reference/automations#get); PHP [`automations->get`](https://openemail.uk/docs/php/reference/automations#get); Go [`Automations.Get`](https://openemail.uk/docs/go/reference/automations#get); Java [`automations().get`](https://openemail.uk/docs/java/reference/automations#get); C# [`Automations.GetAsync`](https://openemail.uk/docs/csharp/reference/automations#get); CLI [`openemail automations get`](https://openemail.uk/docs/cli/reference/automations#automations-get); MCP [`getAutomation`](https://openemail.uk/docs/mcp/tools/automations#getAutomation).

### `PATCH /automations/{id}`

Update an automation

A partial update. `definition` replaces the draft whole, and a live automation keeps running its published version until `POST /automations/{id}/publish`. `settings` is merged field by field and takes effect at once.

Read the automation, change it and send `expectedUpdatedAt` with the `updatedAt` you read: if someone saved it in between, your change is refused instead of written over theirs.

Requires the `automations:write` scope.

- Scopes: `automations:write`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Request body**

- `name` (`string`, 1 to 120 characters): What the workspace calls the automation, trimmed, 1 to 120 characters. Contacts never see it, and it need not be unique.
- `description` (`string`, nullable, up to 500 characters): A note for the workspace, at most 500 characters. Contacts never see it.
- `definition` (`object`): What starts an automation and what it then does. Paths never join or loop: every step is reached from exactly one place, and a definition holds at most 50 steps.
  - `trigger` (`object`, required, nullable): What puts a contact into the automation. Null in a draft that has none yet, and a draft cannot be published without one.
    - `kind` (`string`, required, one of `"audience_joined"`)
    - `audienceId` (`string`, required, up to 64 characters)
    - `includeImported` (`boolean`, required)
  - `entry` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`): The `key` of the first step, or null while the draft has no steps.
  - `steps` (`object[]`, required, up to 50 items): Every step, in any order. Each has a unique `key`, and names the step after it by key in `next`, or in `yes` and `no` for a branch. Null ends the path there.
    - `key` (`string`, required, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `name` (`string`, up to 80 characters)
    - `kind` (`string`, required, one of `"send_email"`)
    - `next` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `templateId` (`string`, required, up to 64 characters)
    - `templateVersion` (`integer`, required, nullable, at least 1)
    - `from` (`object`, required)
      - `email` (`string`, required, up to 320 characters)
      - `name` (`string`, up to 120 characters)
    - `replyTo` (`string`, required, nullable, up to 320 characters)
    - `subject` (`string`, required, nullable, up to 300 characters)
    - `props` (`Record<string, object>`, required)
- `settings` (`object`): Merged field by field into the stored settings, and applied at once, without publishing. Send only the fields you are changing.
  - `timezone` (`string`, 1 to 64 characters)
  - `sendWindow` (`object`, nullable)
    - `days` (`integer[]`, required, up to 7 items)
    - `startMinute` (`integer`, required, at least 0, at most 1440)
    - `endMinute` (`integer`, required, at least 0, at most 1440)
  - `reentryDays` (`integer`, nullable, at least 1, at most 3650)
  - `exitOnLeave` (`boolean`)
  - `listAudienceId` (`string`, nullable, up to 64 characters)
- `expectedUpdatedAt` (`string`, format `date-time`): The `updatedAt` you read. When the automation was saved since, the call is refused with 409 `version_conflict` and nothing is written.

**Returns**

- `200` `Automation`: Saved.

**Errors**

- `404`: `automation_not_found` when the id names no automation you can reach.
- `409`: `version_conflict`: the automation was saved after `expectedUpdatedAt`. `automation_archived`: an archived automation cannot be changed. Nothing was written.
- `422`: `invalid_parameter` for a value of the wrong shape, `unknown_parameter` for a key the body does not take, or `invalid_automation` when the definition breaks a rule of its structure or `settings.listAudienceId` names no audience.
- The errors every operation can return: `400`, `401`, `403`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.update()`](https://openemail.uk/docs/sdk/reference/automations#update); Python [`automations.update()`](https://openemail.uk/docs/python/reference/automations#update); Ruby [`automations.update`](https://openemail.uk/docs/ruby/reference/automations#update); PHP [`automations->update`](https://openemail.uk/docs/php/reference/automations#update); Go [`Automations.Update`](https://openemail.uk/docs/go/reference/automations#update); Java [`automations().update`](https://openemail.uk/docs/java/reference/automations#update); C# [`Automations.UpdateAsync`](https://openemail.uk/docs/csharp/reference/automations#update); CLI [`openemail automations update`](https://openemail.uk/docs/cli/reference/automations#automations-update); MCP [`updateAutomation`](https://openemail.uk/docs/mcp/tools/automations#updateAutomation).

### `DELETE /automations/{id}`

Delete an automation

Deletes the automation with its versions, its enrollments and its statistics. Contacts in it stop at once and get nothing more. The emails it already sent stay in `GET /emails`.

There is no undo. To retire one and keep its history, use `POST /automations/{id}/archive`.

Requires the `automations:write` scope.

- Scopes: `automations:write`.
- Asks an OAuth access token for a verification code.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Returns**

- `200` `DeletedAutomation`: Deleted.

**Errors**

- `403`: The key lacks the scope, or may not send as that address. `step_up_required`: the call was made with an OAuth access token that has not been verified in the last 60 minutes. Ask for a code with `POST /security/step-up`, send it to `POST /security/step-up/verify`, then repeat the call. The person can also choose Allow changes for 60 minutes on the app in Account settings, Connected apps, on the OpenEmail website. An API key is never asked for a code.
- `404`: `automation_not_found` when the id names no automation you can reach.
- The errors every operation can return: `400`, `401`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.delete()`](https://openemail.uk/docs/sdk/reference/automations#delete); Python [`automations.delete()`](https://openemail.uk/docs/python/reference/automations#delete); Ruby [`automations.delete`](https://openemail.uk/docs/ruby/reference/automations#delete); PHP [`automations->delete`](https://openemail.uk/docs/php/reference/automations#delete); Go [`Automations.Delete`](https://openemail.uk/docs/go/reference/automations#delete); Java [`automations().delete`](https://openemail.uk/docs/java/reference/automations#delete); C# [`Automations.DeleteAsync`](https://openemail.uk/docs/csharp/reference/automations#delete); CLI [`openemail automations delete`](https://openemail.uk/docs/cli/reference/automations#automations-delete); MCP [`deleteAutomation`](https://openemail.uk/docs/mcp/tools/automations#deleteAutomation).

### `POST /automations/{id}/publish`

Publish an automation

Saves the draft as the next version and sets the automation `live`, so its trigger starts taking contacts in. Contacts already in it stay on the version they entered on. Publishing a paused automation turns it back on. No body.

The draft has to be complete: a trigger, at least one step, every email with a published template that declares `unsubscribeUrl` and a from address the caller may send as, and every audience, form and webhook it names still there. The automation sends as whoever published it, so it pauses itself if that API key is revoked.

Requires the `automations:write` and `emails:send` scopes.

- Scopes: `automations:write`, `emails:send`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Returns**

- `200` `Automation`: Published and live.

**Errors**

- `403`: `automation_limit_reached` when the plan covers no more live automations, or `insufficient_scope`.
- `404`: `automation_not_found` when the id names no automation you can reach.
- `409`: `automation_archived`: an archived automation cannot be published.
- `422`: `invalid_automation` when the draft cannot run: the error carries `problems`, every problem that blocks it, each with a `code`, the `path` of the field, the `stepKey` and a `message`.
- The errors every operation can return: `400`, `401`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.publish()`](https://openemail.uk/docs/sdk/reference/automations#publish); Python [`automations.publish()`](https://openemail.uk/docs/python/reference/automations#publish); Ruby [`automations.publish`](https://openemail.uk/docs/ruby/reference/automations#publish); PHP [`automations->publish`](https://openemail.uk/docs/php/reference/automations#publish); Go [`Automations.Publish`](https://openemail.uk/docs/go/reference/automations#publish); Java [`automations().publish`](https://openemail.uk/docs/java/reference/automations#publish); C# [`Automations.PublishAsync`](https://openemail.uk/docs/csharp/reference/automations#publish); CLI [`openemail automations publish`](https://openemail.uk/docs/cli/reference/automations#automations-publish); MCP [`publishAutomation`](https://openemail.uk/docs/mcp/tools/automations#publishAutomation).

### `POST /automations/{id}/pause`

Pause an automation

Stops a live automation. Nobody new enters, and everyone in it stays where they are and moves on when it is resumed. Pausing a paused automation changes nothing. No body.

Requires the `automations:write` scope.

- Scopes: `automations:write`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Returns**

- `200` `Automation`: Paused.

**Errors**

- `404`: `automation_not_found` when the id names no automation you can reach.
- `409`: `automation_not_published`: a draft that was never published cannot be paused. `automation_archived`: neither can an archived one.
- The errors every operation can return: `400`, `401`, `403`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.pause()`](https://openemail.uk/docs/sdk/reference/automations#pause); Python [`automations.pause()`](https://openemail.uk/docs/python/reference/automations#pause); Ruby [`automations.pause`](https://openemail.uk/docs/ruby/reference/automations#pause); PHP [`automations->pause`](https://openemail.uk/docs/php/reference/automations#pause); Go [`Automations.Pause`](https://openemail.uk/docs/go/reference/automations#pause); Java [`automations().pause`](https://openemail.uk/docs/java/reference/automations#pause); C# [`Automations.PauseAsync`](https://openemail.uk/docs/csharp/reference/automations#pause); CLI [`openemail automations pause`](https://openemail.uk/docs/cli/reference/automations#automations-pause); MCP [`pauseAutomation`](https://openemail.uk/docs/mcp/tools/automations#pauseAutomation).

### `POST /automations/{id}/resume`

Resume an automation

Turns a paused automation back on with the version it was running, without publishing the draft. Contacts that were held move on, and from now on it sends as the caller. The published version is checked again first, so an automation OpenEmail paused stays paused until what stopped it is fixed. Resuming a live automation changes nothing. No body.

Requires the `automations:write` and `emails:send` scopes.

- Scopes: `automations:write`, `emails:send`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Returns**

- `200` `Automation`: Live again.

**Errors**

- `403`: `automation_limit_reached` when the plan covers no more live automations, or `insufficient_scope`.
- `404`: `automation_not_found` when the id names no automation you can reach.
- `409`: `automation_not_published`: there is no published version to resume. `automation_archived`: an archived automation cannot be resumed.
- `422`: `invalid_automation` when the draft cannot run: the error carries `problems`, every problem that blocks it, each with a `code`, the `path` of the field, the `stepKey` and a `message`.
- The errors every operation can return: `400`, `401`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.resume()`](https://openemail.uk/docs/sdk/reference/automations#resume); Python [`automations.resume()`](https://openemail.uk/docs/python/reference/automations#resume); Ruby [`automations.resume`](https://openemail.uk/docs/ruby/reference/automations#resume); PHP [`automations->resume`](https://openemail.uk/docs/php/reference/automations#resume); Go [`Automations.Resume`](https://openemail.uk/docs/go/reference/automations#resume); Java [`automations().resume`](https://openemail.uk/docs/java/reference/automations#resume); C# [`Automations.ResumeAsync`](https://openemail.uk/docs/csharp/reference/automations#resume); CLI [`openemail automations resume`](https://openemail.uk/docs/cli/reference/automations#automations-resume); MCP [`resumeAutomation`](https://openemail.uk/docs/mcp/tools/automations#resumeAutomation).

### `POST /automations/{id}/archive`

Archive an automation

Retires an automation for good and keeps its history. Everyone in it leaves with the exit reason `archived`, nobody enters again, and it can no longer be changed, published or resumed. Its versions, enrollments and statistics stay readable. Archiving an archived automation changes nothing. No body.

To start again from its steps, use `POST /automations/{id}/duplicate`.

Requires the `automations:write` scope.

- Scopes: `automations:write`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Returns**

- `200` `Automation`: Archived.

**Errors**

- `404`: `automation_not_found` when the id names no automation you can reach.
- The errors every operation can return: `400`, `401`, `403`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.archive()`](https://openemail.uk/docs/sdk/reference/automations#archive); Python [`automations.archive()`](https://openemail.uk/docs/python/reference/automations#archive); Ruby [`automations.archive`](https://openemail.uk/docs/ruby/reference/automations#archive); PHP [`automations->archive`](https://openemail.uk/docs/php/reference/automations#archive); Go [`Automations.Archive`](https://openemail.uk/docs/go/reference/automations#archive); Java [`automations().archive`](https://openemail.uk/docs/java/reference/automations#archive); C# [`Automations.ArchiveAsync`](https://openemail.uk/docs/csharp/reference/automations#archive); CLI [`openemail automations archive`](https://openemail.uk/docs/cli/reference/automations#automations-archive); MCP [`archiveAutomation`](https://openemail.uk/docs/mcp/tools/automations#archiveAutomation).

### `POST /automations/{id}/duplicate`

Duplicate an automation

Makes a new draft with the same draft definition and settings, named after the original with (copy) on the end. The copy has no versions, no contacts and no statistics. No body.

Requires the `automations:write` scope.

- Scopes: `automations:write`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Returns**

- `201` `Automation`: The copy, as a draft.

**Errors**

- `404`: `automation_not_found` when the id names no automation you can reach.
- `422`: `workspace_limit_reached` when the workspace holds the most automations it may.
- The errors every operation can return: `400`, `401`, `403`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.duplicate()`](https://openemail.uk/docs/sdk/reference/automations#duplicate); Python [`automations.duplicate()`](https://openemail.uk/docs/python/reference/automations#duplicate); Ruby [`automations.duplicate`](https://openemail.uk/docs/ruby/reference/automations#duplicate); PHP [`automations->duplicate`](https://openemail.uk/docs/php/reference/automations#duplicate); Go [`Automations.Duplicate`](https://openemail.uk/docs/go/reference/automations#duplicate); Java [`automations().duplicate`](https://openemail.uk/docs/java/reference/automations#duplicate); C# [`Automations.DuplicateAsync`](https://openemail.uk/docs/csharp/reference/automations#duplicate); CLI [`openemail automations duplicate`](https://openemail.uk/docs/cli/reference/automations#automations-duplicate); MCP [`duplicateAutomation`](https://openemail.uk/docs/mcp/tools/automations#duplicateAutomation).

### `POST /automations/{id}/test`

Send a test of one email step

Sends the email of one step of the draft to one address, so you can read it before publishing. Contact values are filled from a sample contact, values that come from an event are left empty, and the subject starts with [Test]. It counts toward the monthly sends, is not tracked and enrolls nobody.

Requires the `automations:write` and `emails:send` scopes.

- Scopes: `automations:write`, `emails:send`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Request body**

- `stepKey` (`string`, required, 1 to 64 characters): The `key` of the email step to send, from the draft `definition`.
- `to` (`string`, up to 320 characters, format `email`): Where the test goes. Left out, it goes to the account email of the person the key or app acts for.

**Returns**

- `200` `AutomationTest`: Sent.

**Errors**

- `403`: `from_address_forbidden` when the caller may not send as the from address of the step, or `insufficient_scope`.
- `404`: `automation_not_found` when the id names no automation you can reach.
- `409`: `domain_not_sendable` when the domain of the from address cannot send yet.
- `422`: `invalid_automation` when `stepKey` names no step, the step is not an email, or its template cannot be sent, with `param` set to `stepKey`.
- `429`: `send_quota_exceeded`: the monthly sends of the plan are used up.
- The errors every operation can return: `400`, `401`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.sendTest()`](https://openemail.uk/docs/sdk/reference/automations#sendTest); Python [`automations.send_test()`](https://openemail.uk/docs/python/reference/automations#sendTest); Ruby [`automations.send_test`](https://openemail.uk/docs/ruby/reference/automations#sendTest); PHP [`automations->sendTest`](https://openemail.uk/docs/php/reference/automations#sendTest); Go [`Automations.SendTest`](https://openemail.uk/docs/go/reference/automations#sendTest); Java [`automations().sendTest`](https://openemail.uk/docs/java/reference/automations#sendTest); C# [`Automations.SendTestAsync`](https://openemail.uk/docs/csharp/reference/automations#sendTest); CLI [`openemail automations send-test`](https://openemail.uk/docs/cli/reference/automations#automations-send-test); MCP [`sendAutomationTest`](https://openemail.uk/docs/mcp/tools/automations#sendAutomationTest).

### `GET /automations/{id}/versions`

List the versions of an automation

Every published version, the newest first, each with its whole definition. `current` marks the one that is running. Not paginated. A draft that was never published has none.

Requires the `automations:read` scope.

- Scopes: `automations:read`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Returns**

- `200` `AutomationVersionList`: The versions.

**Errors**

- `404`: `automation_not_found` when the id names no automation you can reach.
- The errors every operation can return: `400`, `401`, `403`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.listVersions()`](https://openemail.uk/docs/sdk/reference/automations#listVersions); Python [`automations.list_versions()`](https://openemail.uk/docs/python/reference/automations#listVersions); Ruby [`automations.list_versions`](https://openemail.uk/docs/ruby/reference/automations#listVersions); PHP [`automations->listVersions`](https://openemail.uk/docs/php/reference/automations#listVersions); Go [`Automations.ListVersions`](https://openemail.uk/docs/go/reference/automations#listVersions); Java [`automations().listVersions`](https://openemail.uk/docs/java/reference/automations#listVersions); C# [`Automations.ListVersionsAsync`](https://openemail.uk/docs/csharp/reference/automations#listVersions); CLI [`openemail automations list-versions`](https://openemail.uk/docs/cli/reference/automations#automations-list-versions); MCP [`listAutomationVersions`](https://openemail.uk/docs/mcp/tools/automations#listAutomationVersions).

### `POST /automations/{id}/versions/{version}/restore`

Restore a version into the draft

Copies the definition of an earlier version into the draft, replacing what the draft holds. Nothing that is running changes until `POST /automations/{id}/publish`. No body.

Requires the `automations:write` scope.

- Scopes: `automations:write`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.
- `version` (`integer`, required, at least 1): The number of the version, as `GET /automations/{id}/versions` lists it.

**Returns**

- `200` `Automation`: The automation, with the version in its draft.

**Errors**

- `404`: `automation_not_found` when the id names no automation you can reach, or, with `param` set to `version`, when it has no such version.
- `409`: `automation_archived`: an archived automation cannot be changed.
- The errors every operation can return: `400`, `401`, `403`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.restoreVersion()`](https://openemail.uk/docs/sdk/reference/automations#restoreVersion); Python [`automations.restore_version()`](https://openemail.uk/docs/python/reference/automations#restoreVersion); Ruby [`automations.restore_version`](https://openemail.uk/docs/ruby/reference/automations#restoreVersion); PHP [`automations->restoreVersion`](https://openemail.uk/docs/php/reference/automations#restoreVersion); Go [`Automations.RestoreVersion`](https://openemail.uk/docs/go/reference/automations#restoreVersion); Java [`automations().restoreVersion`](https://openemail.uk/docs/java/reference/automations#restoreVersion); C# [`Automations.RestoreVersionAsync`](https://openemail.uk/docs/csharp/reference/automations#restoreVersion); CLI [`openemail automations restore-version`](https://openemail.uk/docs/cli/reference/automations#automations-restore-version); MCP [`restoreAutomationVersion`](https://openemail.uk/docs/mcp/tools/automations#restoreAutomationVersion).

### `GET /automations/{id}/stats`

How an automation has performed

Totals for a window, the same numbers step by step, and a point per day. The steps are those of the published version, or of the draft when nothing is published. `active` and `waiting` are counted at the moment of the read, whatever the window.

Requires the `automations:read` scope.

- Scopes: `automations:read`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Query parameters**

- `since` (`string`, format `date-time`): Where the window starts, as an ISO 8601 instant. Left out, 30 days before `until`. It reaches back at most 366 days before `until`.
- `until` (`string`, format `date-time`): Where the window ends, as an ISO 8601 instant. Left out, now.

**Returns**

- `200` `AutomationStats`: The numbers for the window.

**Errors**

- `404`: `automation_not_found` when the id names no automation you can reach.
- `422`: `invalid_parameter` when a time is not an ISO 8601 instant or `until` is not after `since`.
- The errors every operation can return: `400`, `401`, `403`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.stats()`](https://openemail.uk/docs/sdk/reference/automations#stats); Python [`automations.stats()`](https://openemail.uk/docs/python/reference/automations#stats); Ruby [`automations.stats`](https://openemail.uk/docs/ruby/reference/automations#stats); PHP [`automations->stats`](https://openemail.uk/docs/php/reference/automations#stats); Go [`Automations.Stats`](https://openemail.uk/docs/go/reference/automations#stats); Java [`automations().stats`](https://openemail.uk/docs/java/reference/automations#stats); C# [`Automations.StatsAsync`](https://openemail.uk/docs/csharp/reference/automations#stats); CLI [`openemail automations stats`](https://openemail.uk/docs/cli/reference/automations#automations-stats); MCP [`getAutomationStats`](https://openemail.uk/docs/mcp/tools/automations#getAutomationStats).

### `GET /automations/{id}/enrollments`

List the contacts in an automation

Everyone who is in the automation or has been, the most recent entry first: where each contact is, what they are waiting for and how it ended.

Requires the `automations:read` scope.

- Scopes: `automations:read`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Query parameters**

- `status` (`string`, one of `"active"`, `"completed"`, `"exited"`): Only enrollments in this state.
- `stepKey` (`string`, 1 to 64 characters): Only contacts at the step with this `key`.
- `q` (`string`, up to 200 characters): Only contacts whose address or name contains this text, compared without case.
- `limit` (`integer`, at least 1, at most 200, default `50`): Rows per page, 1 to 200.
- `cursor` (`string`): The id of the last enrollment on the previous page. Keyset, not offset: pass the previous page's `nextCursor`. One that names nothing in this list is a 400 `invalid_cursor`.

**Returns**

- `200` `AutomationEnrollmentList`: A page of enrollments.

**Errors**

- `404`: `automation_not_found` when the id names no automation you can reach.
- The errors every operation can return: `400`, `401`, `403`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.listEnrollments()`](https://openemail.uk/docs/sdk/reference/automations#listEnrollments), [`automations.listAllEnrollments()`](https://openemail.uk/docs/sdk/reference/automations#listAllEnrollments), [`automations.iterateEnrollments()`](https://openemail.uk/docs/sdk/reference/automations#iterateEnrollments); Python [`automations.list_enrollments()`](https://openemail.uk/docs/python/reference/automations#listEnrollments), [`automations.list_all_enrollments()`](https://openemail.uk/docs/python/reference/automations#listAllEnrollments), [`automations.iterate_enrollments()`](https://openemail.uk/docs/python/reference/automations#iterateEnrollments); Ruby [`automations.list_enrollments`](https://openemail.uk/docs/ruby/reference/automations#listEnrollments), [`automations.list_all_enrollments`](https://openemail.uk/docs/ruby/reference/automations#listAllEnrollments), [`automations.iterate_enrollments`](https://openemail.uk/docs/ruby/reference/automations#iterateEnrollments); PHP [`automations->listEnrollments`](https://openemail.uk/docs/php/reference/automations#listEnrollments), [`automations->listAllEnrollments`](https://openemail.uk/docs/php/reference/automations#listAllEnrollments), [`automations->iterateEnrollments`](https://openemail.uk/docs/php/reference/automations#iterateEnrollments); Go [`Automations.ListEnrollments`](https://openemail.uk/docs/go/reference/automations#listEnrollments), [`Automations.ListAllEnrollments`](https://openemail.uk/docs/go/reference/automations#listAllEnrollments), [`Automations.IterateEnrollments`](https://openemail.uk/docs/go/reference/automations#iterateEnrollments); Java [`automations().listEnrollments`](https://openemail.uk/docs/java/reference/automations#listEnrollments), [`automations().listAllEnrollments`](https://openemail.uk/docs/java/reference/automations#listAllEnrollments), [`automations().iterateEnrollments`](https://openemail.uk/docs/java/reference/automations#iterateEnrollments); C# [`Automations.ListEnrollmentsAsync`](https://openemail.uk/docs/csharp/reference/automations#listEnrollments), [`Automations.ListAllEnrollmentsAsync`](https://openemail.uk/docs/csharp/reference/automations#listAllEnrollments), [`Automations.IterateEnrollmentsAsync`](https://openemail.uk/docs/csharp/reference/automations#iterateEnrollments); CLI [`openemail automations list-enrollments`](https://openemail.uk/docs/cli/reference/automations#automations-list-enrollments); MCP [`listAutomationEnrollments`](https://openemail.uk/docs/mcp/tools/automations#listAutomationEnrollments).

### `POST /automations/{id}/enrollments`

Enroll a contact in an automation

Puts one contact into a live automation at its first step, whatever its trigger is. The contact has to exist already. `data` gives the steps the values they would otherwise read from an event.

A contact is in an automation once at a time, and comes back in only after `settings.reentryDays`. An address on the suppression list, or one that unsubscribed from the audience the automation sends through, is refused.

Requires the `automations:write` scope.

- Scopes: `automations:write`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.

**Request body**

- `contactId` (`string`, 1 to 64 characters): The contact, by id. Send this or `email`, never both.
- `email` (`string`, up to 320 characters, format `email`): The contact, by email address. Send this or `contactId`, never both.
- `data` (`Record<string, any>`): Values the steps can read wherever a value comes from the event, such as an order number for an email. At most 50 keys and 4 KB of JSON.

**Returns**

- `201` `AutomationEnrollment`: Enrolled, and due at the first step.

**Errors**

- `404`: `automation_not_found`, or `contact_not_found` when no contact has that id or address.
- `409`: `automation_not_live` when it is a draft, paused or archived. `already_enrolled` when the contact is in it or finished it too recently. `contact_unreachable` when the address is suppressed or unsubscribed.
- `422`: `invalid_parameter` when neither or both of `contactId` and `email` are sent, or `data` is too large.
- The errors every operation can return: `400`, `401`, `403`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.enroll()`](https://openemail.uk/docs/sdk/reference/automations#enroll); Python [`automations.enroll()`](https://openemail.uk/docs/python/reference/automations#enroll); Ruby [`automations.enroll`](https://openemail.uk/docs/ruby/reference/automations#enroll); PHP [`automations->enroll`](https://openemail.uk/docs/php/reference/automations#enroll); Go [`Automations.Enroll`](https://openemail.uk/docs/go/reference/automations#enroll); Java [`automations().enroll`](https://openemail.uk/docs/java/reference/automations#enroll); C# [`Automations.EnrollAsync`](https://openemail.uk/docs/csharp/reference/automations#enroll); CLI [`openemail automations enroll`](https://openemail.uk/docs/cli/reference/automations#automations-enroll); MCP [`enrollInAutomation`](https://openemail.uk/docs/mcp/tools/automations#enrollInAutomation).

### `GET /automations/{id}/enrollments/{enrollmentId}`

Retrieve an enrollment

One contact's way through the automation: the enrollment, plus `runs`, what each step did for them, oldest first, up to 200.

Requires the `automations:read` scope.

- Scopes: `automations:read`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.
- `enrollmentId` (`string`, required): The enrollment, such as `aen_2b8d4f6a1c3e5079b6d8f0a2`, as `GET /automations/{id}/enrollments` lists it.

**Returns**

- `200` `AutomationEnrollmentDetail`: The enrollment with its runs.

**Errors**

- `404`: `automation_not_found` or `automation_enrollment_not_found`.
- The errors every operation can return: `400`, `401`, `403`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.getEnrollment()`](https://openemail.uk/docs/sdk/reference/automations#getEnrollment); Python [`automations.get_enrollment()`](https://openemail.uk/docs/python/reference/automations#getEnrollment); Ruby [`automations.get_enrollment`](https://openemail.uk/docs/ruby/reference/automations#getEnrollment); PHP [`automations->getEnrollment`](https://openemail.uk/docs/php/reference/automations#getEnrollment); Go [`Automations.GetEnrollment`](https://openemail.uk/docs/go/reference/automations#getEnrollment); Java [`automations().getEnrollment`](https://openemail.uk/docs/java/reference/automations#getEnrollment); C# [`Automations.GetEnrollmentAsync`](https://openemail.uk/docs/csharp/reference/automations#getEnrollment); CLI [`openemail automations get-enrollment`](https://openemail.uk/docs/cli/reference/automations#automations-get-enrollment); MCP [`getAutomationEnrollment`](https://openemail.uk/docs/mcp/tools/automations#getAutomationEnrollment).

### `POST /automations/{id}/enrollments/{enrollmentId}/exit`

Take a contact out of an automation

Ends an active enrollment at once with the exit reason `removed`. The contact gets nothing more from this automation, and stays in the contacts and in their audiences. No body.

Requires the `automations:write` scope.

- Scopes: `automations:write`.

**Path parameters**

- `id` (`string`, required): The automation, such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`, as `GET /automations` lists it.
- `enrollmentId` (`string`, required): The enrollment, such as `aen_2b8d4f6a1c3e5079b6d8f0a2`, as `GET /automations/{id}/enrollments` lists it.

**Returns**

- `200` `AutomationEnrollment`: The enrollment, exited.

**Errors**

- `404`: `automation_not_found` or `automation_enrollment_not_found`.
- `409`: `automation_enrollment_finished`: the contact already completed or left.
- The errors every operation can return: `400`, `401`, `403`, `422`, `500`, described in the [error catalog](https://openemail.uk/docs/api/errors).

Also available in: TypeScript [`automations.exitEnrollment()`](https://openemail.uk/docs/sdk/reference/automations#exitEnrollment); Python [`automations.exit_enrollment()`](https://openemail.uk/docs/python/reference/automations#exitEnrollment); Ruby [`automations.exit_enrollment`](https://openemail.uk/docs/ruby/reference/automations#exitEnrollment); PHP [`automations->exitEnrollment`](https://openemail.uk/docs/php/reference/automations#exitEnrollment); Go [`Automations.ExitEnrollment`](https://openemail.uk/docs/go/reference/automations#exitEnrollment); Java [`automations().exitEnrollment`](https://openemail.uk/docs/java/reference/automations#exitEnrollment); C# [`Automations.ExitEnrollmentAsync`](https://openemail.uk/docs/csharp/reference/automations#exitEnrollment); CLI [`openemail automations exit-enrollment`](https://openemail.uk/docs/cli/reference/automations#automations-exit-enrollment); MCP [`removeFromAutomation`](https://openemail.uk/docs/mcp/tools/automations#removeFromAutomation).

### Objects

#### `Automation`

`object`

- `object` (`string`, required, one of `"automation"`)
- `id` (`string`, required): The id of the automation. It never changes.
- `name` (`string`, required): What the workspace calls it. Contacts never see it.
- `description` (`string`, required, nullable): A note for the workspace.
- `status` (`string`, required, one of `"draft"`, `"live"`, `"paused"`, `"archived"`): `draft` until the first publish. `live` takes contacts in and moves them along. `paused` takes nobody in and holds everyone where they are. `archived` is retired for good and everyone in it has left.
- `triggerKind` (`string`, required, nullable, one of `"audience_joined"`, `"form_submitted"`, `"event"`, `"date"`, `"manual"`): What starts the draft, or null while it has no trigger.
- `stepCount` (`integer`, required, at least 0): How many steps the draft holds.
- `emailCount` (`integer`, required, at least 0): How many of those steps send an email.
- `hasUnpublishedChanges` (`boolean`, required): True when the draft differs from the version that is running.
- `publishedVersion` (`integer`, required, nullable): The number of the version that is running. Null until the first publish.
- `pausedReason` (`string`, required, nullable, one of `"manual"`, `"sender_refused"`, `"key_revoked"`, `"template_unavailable"`, `"audience_missing"`, `"plan_limit"`): Why it is paused. `manual` is a person. The others are OpenEmail stopping it: `sender_refused` when its from address can no longer send, `key_revoked` when the API key that published it was revoked, expired or turned off, `template_unavailable` when one of its templates can no longer be sent, `audience_missing` when an audience it depends on is gone, and `plan_limit` when the plan no longer covers this many live automations. Null while it is not paused.
- `lastError` (`string`, required, nullable): What went wrong the last time OpenEmail paused it, in a sentence. Null otherwise.
- `counts` (`object`, required): Contacts, counted at the moment of the read.
  - `active` (`integer`, required, at least 0): In the automation now.
  - `completed` (`integer`, required, at least 0): Reached the end of a path, in all its time.
  - `exited` (`integer`, required, at least 0): Left before the end, in all its time.
- `createdBy` (`string`, required, nullable): The user id of the person who made it. Null when that account is gone.
- `publishedAt` (`string`, required, nullable, format `date-time`): The last publish. Null until the first.
- `pausedAt` (`string`, required, nullable, format `date-time`): When it was paused. Null while it is not.
- `archivedAt` (`string`, required, nullable, format `date-time`): When it was archived. Null otherwise.
- `createdAt` (`string`, required, format `date-time`): When it was made.
- `updatedAt` (`string`, required, format `date-time`): The last save. Send it back as `expectedUpdatedAt` so a change never overwrites one you have not seen.
- `definition` (`object`, required): What starts an automation and what it then does. Paths never join or loop: every step is reached from exactly one place, and a definition holds at most 50 steps.
  - `trigger` (`object`, required, nullable): What puts a contact into the automation. Null in a draft that has none yet, and a draft cannot be published without one.
    - `kind` (`string`, required, one of `"audience_joined"`)
    - `audienceId` (`string`, required, up to 64 characters)
    - `includeImported` (`boolean`, required)
  - `entry` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`): The `key` of the first step, or null while the draft has no steps.
  - `steps` (`object[]`, required, up to 50 items): Every step, in any order. Each has a unique `key`, and names the step after it by key in `next`, or in `yes` and `no` for a branch. Null ends the path there.
    - `key` (`string`, required, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `name` (`string`, up to 80 characters)
    - `kind` (`string`, required, one of `"send_email"`)
    - `next` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `templateId` (`string`, required, up to 64 characters)
    - `templateVersion` (`integer`, required, nullable, at least 1)
    - `from` (`object`, required)
      - `email` (`string`, required, up to 320 characters)
      - `name` (`string`, up to 120 characters)
    - `replyTo` (`string`, required, nullable, up to 320 characters)
    - `subject` (`string`, required, nullable, up to 300 characters)
    - `props` (`Record<string, object>`, required)
- `published` (`object`, required, nullable): What starts an automation and what it then does. Paths never join or loop: every step is reached from exactly one place, and a definition holds at most 50 steps.
  - `trigger` (`object`, required, nullable): What puts a contact into the automation. Null in a draft that has none yet, and a draft cannot be published without one.
    - `kind` (`string`, required, one of `"audience_joined"`)
    - `audienceId` (`string`, required, up to 64 characters)
    - `includeImported` (`boolean`, required)
  - `entry` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`): The `key` of the first step, or null while the draft has no steps.
  - `steps` (`object[]`, required, up to 50 items): Every step, in any order. Each has a unique `key`, and names the step after it by key in `next`, or in `yes` and `no` for a branch. Null ends the path there.
    - `key` (`string`, required, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `name` (`string`, up to 80 characters)
    - `kind` (`string`, required, one of `"send_email"`)
    - `next` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `templateId` (`string`, required, up to 64 characters)
    - `templateVersion` (`integer`, required, nullable, at least 1)
    - `from` (`object`, required)
      - `email` (`string`, required, up to 320 characters)
      - `name` (`string`, up to 120 characters)
    - `replyTo` (`string`, required, nullable, up to 320 characters)
    - `subject` (`string`, required, nullable, up to 300 characters)
    - `props` (`Record<string, object>`, required)
- `settings` (`object`, required): How it runs. Settings take effect when they are saved, published or not.
  - `timezone` (`string`, required, 1 to 64 characters): The IANA time zone the sending window and waits until a day and time are read in, such as `Europe/London`. `UTC` until you set one.
  - `sendWindow` (`object`, required, nullable): When emails may go out: `days` of the week (0 is Sunday) and `startMinute` to `endMinute` of the day, counted from midnight. An email due outside it waits for the window to open. Null sends at any time.
    - `days` (`integer[]`, required, up to 7 items)
    - `startMinute` (`integer`, required, at least 0, at most 1440)
    - `endMinute` (`integer`, required, at least 0, at most 1440)
  - `reentryDays` (`integer`, required, nullable, at least 1, at most 3650): How many days after a contact finishes before they may enter again. Null lets each contact through once only.
  - `exitOnLeave` (`boolean`, required): Take a contact out when they leave the audience that started the automation. True by default.
  - `listAudienceId` (`string`, required, nullable, up to 64 characters): The audience an unsubscribe from one of these emails is recorded in. A contact who unsubscribed from it is not sent to and cannot be enrolled. Null uses the audience that starts the automation, or the built-in audience of every contact when no audience starts it.
- `problems` (`AutomationProblem[]`, required): What is wrong with the draft, checked at the moment of the read. Empty when it is ready. A publish checks more: that every template can be sent and that the caller may send from every from address.

#### `AutomationEnrollment`

`object`

- `object` (`string`, required, one of `"automation_enrollment"`)
- `id` (`string`, required): The id of the enrollment.
- `automationId` (`string`, required)
- `version` (`integer`, required, nullable): The published version the contact entered on and stays on. Null when that version is gone.
- `contact` (`object`, required)
  - `id` (`string`, required): The id of the contact.
  - `email` (`string`, required, format `email`)
  - `name` (`string`, required, nullable)
- `status` (`string`, required, one of `"active"`, `"completed"`, `"exited"`): `active` while the contact is in it, `completed` at the end of a path, `exited` when they left early.
- `source` (`string`, required, one of `"trigger"`, `"manual"`, `"api"`): How they got in: `trigger`, by hand in the app (`manual`), or through this API or an app (`api`).
- `stepKey` (`string`, required, nullable): The `key` of the step the contact is at, or was at when they finished. Null before the first step.
- `waiting` (`boolean`, required): True while the contact sits in a wait step.
- `waitingForEvent` (`string`, required, nullable): The event name a wait step is holding out for. Null otherwise.
- `heldFor` (`string`, required, nullable, one of `"window"`, `"quota"`, `"lane"`, `"retry"`): Why a step that is due has not run: `window` for the sending window, `quota` for the monthly sends, `lane` when the broadcast lane of the workspace is paused, `retry` after a failure that is tried again. Null when nothing holds it.
- `nextRunAt` (`string`, required, nullable, format `date-time`): When the contact moves next. Null once they finished.
- `exitReason` (`string`, required, nullable, one of `"completed"`, `"exit_step"`, `"unsubscribed"`, `"suppressed"`, `"left_audience"`, `"removed"`, `"archived"`, `"failed"`): How it ended: `completed`, an exit step (`exit_step`), `unsubscribed`, `suppressed`, `left_audience`, taken out by a person or a call (`removed`), the automation was `archived`, or a step `failed` for good. Null while active.
- `lastError` (`string`, required, nullable): The last failure of a step, in a sentence.
- `startedAt` (`string`, required, format `date-time`): When the contact entered.
- `finishedAt` (`string`, required, nullable, format `date-time`): When they completed or left. Null while active.
- `updatedAt` (`string`, required, format `date-time`): The last time anything about the enrollment changed.

#### `AutomationEnrollmentDetail`

`object`

- `object` (`string`, required, one of `"automation_enrollment"`)
- `id` (`string`, required): The id of the enrollment.
- `automationId` (`string`, required)
- `version` (`integer`, required, nullable): The published version the contact entered on and stays on. Null when that version is gone.
- `contact` (`object`, required)
  - `id` (`string`, required): The id of the contact.
  - `email` (`string`, required, format `email`)
  - `name` (`string`, required, nullable)
- `status` (`string`, required, one of `"active"`, `"completed"`, `"exited"`): `active` while the contact is in it, `completed` at the end of a path, `exited` when they left early.
- `source` (`string`, required, one of `"trigger"`, `"manual"`, `"api"`): How they got in: `trigger`, by hand in the app (`manual`), or through this API or an app (`api`).
- `stepKey` (`string`, required, nullable): The `key` of the step the contact is at, or was at when they finished. Null before the first step.
- `waiting` (`boolean`, required): True while the contact sits in a wait step.
- `waitingForEvent` (`string`, required, nullable): The event name a wait step is holding out for. Null otherwise.
- `heldFor` (`string`, required, nullable, one of `"window"`, `"quota"`, `"lane"`, `"retry"`): Why a step that is due has not run: `window` for the sending window, `quota` for the monthly sends, `lane` when the broadcast lane of the workspace is paused, `retry` after a failure that is tried again. Null when nothing holds it.
- `nextRunAt` (`string`, required, nullable, format `date-time`): When the contact moves next. Null once they finished.
- `exitReason` (`string`, required, nullable, one of `"completed"`, `"exit_step"`, `"unsubscribed"`, `"suppressed"`, `"left_audience"`, `"removed"`, `"archived"`, `"failed"`): How it ended: `completed`, an exit step (`exit_step`), `unsubscribed`, `suppressed`, `left_audience`, taken out by a person or a call (`removed`), the automation was `archived`, or a step `failed` for good. Null while active.
- `lastError` (`string`, required, nullable): The last failure of a step, in a sentence.
- `startedAt` (`string`, required, format `date-time`): When the contact entered.
- `finishedAt` (`string`, required, nullable, format `date-time`): When they completed or left. Null while active.
- `updatedAt` (`string`, required, format `date-time`): The last time anything about the enrollment changed.
- `runs` (`object[]`, required): What each step did for the contact, oldest first.
  - `id` (`string`, required)
  - `stepKey` (`string`, required): The `key` of the step that ran.
  - `kind` (`string`, required, one of `"send_email"`, `"wait"`, `"branch"`, `"add_to_audience"`, `"remove_from_audience"`, `"update_field"`, `"webhook"`, `"exit"`)
  - `outcome` (`string`, required, one of `"sent"`, `"skipped"`, `"waited"`, `"yes"`, `"no"`, `"added"`, `"removed"`, `"updated"`, `"called"`, `"exited"`, `"failed"`): What happened: an email `sent`, a wait `waited` out, a branch answered `yes` or `no`, the contact `added` to or `removed` from an audience, a field `updated`, a webhook `called`, the path `exited`, the step `skipped`, or it `failed`.
  - `emailId` (`string`, required, nullable): The email a send step sent, as `GET /emails` lists it. Null for every other step.
  - `detail` (`string`, required, nullable): Why a step was skipped or failed, or how a wait ended.
  - `createdAt` (`string`, required, format `date-time`): When the step ran.

#### `AutomationEnrollmentList`

`object`

- `object` (`string`, one of `"list"`)
- `data` (`AutomationEnrollment[]`)
- `hasMore` (`boolean`): True when another page follows. Pass `nextCursor` back as `cursor` to read it.
- `nextCursor` (`string`, nullable): The id of the last row on this page, or null on the last page.
- `automationId` (`string`): The automation the enrollments belong to.

#### `AutomationList`

`object`

- `object` (`string`, one of `"list"`)
- `data` (`AutomationSummary[]`)
- `hasMore` (`boolean`): True when another page follows. Pass `nextCursor` back as `cursor` to read it.
- `nextCursor` (`string`, nullable): The id of the last row on this page, or null on the last page.

#### `AutomationProblem`

`object`

- `code` (`string`, required, one of `"trigger_missing"`, `"trigger_audience_missing"`, `"trigger_form_missing"`, `"trigger_event_invalid"`, `"trigger_filter_invalid"`, `"no_steps"`, `"too_many_steps"`, `"too_deep"`, `"duplicate_key"`, `"unknown_target"`, `"shared_step"`, `"unreachable"`, `"template_missing"`, `"sender_missing"`, `"sender_invalid"`, `"reply_to_invalid"`, `"wait_invalid"`, `"wait_too_long"`, `"wait_days_missing"`, `"event_name_invalid"`, `"condition_step_missing"`, `"audience_missing"`, `"field_invalid"`, `"value_missing"`, `"webhook_missing"`, `"list_audience_missing"`, `"timezone_invalid"`, `"window_invalid"`, `"template_not_found"`, `"template_not_published"`, `"template_unsubscribe_missing"`, `"template_prop_missing"`, `"template_name_required"`, `"sender_refused"`, `"audience_not_found"`, `"audience_is_everyone"`, `"form_not_found"`, `"webhook_not_found"`, `"opens_unreliable"`): What is wrong, for a program to branch on.
- `path` (`string`, required): The field at fault, dotted, such as `definition.steps.2.templateId` or `settings.timezone`.
- `stepKey` (`string`, required, nullable): The `key` of the step at fault. Null when it is the trigger or a setting.
- `message` (`string`, required): The problem in a sentence a person can act on.
- `blocking` (`boolean`, required): True when it stops a publish. False for a warning, such as a branch on opens.

#### `AutomationStarter`

`object`

- `object` (`string`, required, one of `"automation_starter"`)
- `slug` (`string`, required): What `POST /automations` takes as `starter`.
- `name` (`string`, required)
- `description` (`string`, required): What the starter is for, in a sentence.
- `definition` (`object`, required): What starts an automation and what it then does. Paths never join or loop: every step is reached from exactly one place, and a definition holds at most 50 steps.
  - `trigger` (`object`, required, nullable): What puts a contact into the automation. Null in a draft that has none yet, and a draft cannot be published without one.
    - `kind` (`string`, required, one of `"audience_joined"`)
    - `audienceId` (`string`, required, up to 64 characters)
    - `includeImported` (`boolean`, required)
  - `entry` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`): The `key` of the first step, or null while the draft has no steps.
  - `steps` (`object[]`, required, up to 50 items): Every step, in any order. Each has a unique `key`, and names the step after it by key in `next`, or in `yes` and `no` for a branch. Null ends the path there.
    - `key` (`string`, required, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `name` (`string`, up to 80 characters)
    - `kind` (`string`, required, one of `"send_email"`)
    - `next` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `templateId` (`string`, required, up to 64 characters)
    - `templateVersion` (`integer`, required, nullable, at least 1)
    - `from` (`object`, required)
      - `email` (`string`, required, up to 320 characters)
      - `name` (`string`, up to 120 characters)
    - `replyTo` (`string`, required, nullable, up to 320 characters)
    - `subject` (`string`, required, nullable, up to 300 characters)
    - `props` (`Record<string, object>`, required)

#### `AutomationStarterList`

`object`

- `object` (`string`, one of `"list"`)
- `data` (`AutomationStarter[]`)

#### `AutomationStats`

`object`

- `object` (`string`, required, one of `"automation_stats"`)
- `automationId` (`string`, required)
- `since` (`string`, required, format `date-time`): Where the window starts.
- `until` (`string`, required, format `date-time`): Where the window ends.
- `totals` (`object`, required): The whole automation over the window.
  - `entered` (`integer`, at least 0): Contacts that entered.
  - `active` (`integer`, at least 0): Contacts in it now, whatever the window.
  - `completed` (`integer`, at least 0): Contacts that reached the end of a path.
  - `exited` (`integer`, at least 0): Contacts that left early.
  - `sent` (`integer`, at least 0): Emails sent.
  - `delivered` (`integer`, at least 0): Emails delivered.
  - `opened` (`integer`, at least 0): Emails opened. Many mail apps hide opens, so read it as a floor.
  - `clicked` (`integer`, at least 0): Emails with a click.
  - `bounced` (`integer`, at least 0): Emails that bounced.
  - `complained` (`integer`, at least 0): Emails marked as spam.
  - `unsubscribed` (`integer`, at least 0): Contacts that unsubscribed from one of its emails.
- `steps` (`object[]`, required): The same numbers for each step.
  - `stepKey` (`string`): The `key` of the step.
  - `kind` (`string`, one of `"send_email"`, `"wait"`, `"branch"`, `"add_to_audience"`, `"remove_from_audience"`, `"update_field"`, `"webhook"`, `"exit"`)
  - `entered` (`integer`, at least 0): Contacts that reached the step.
  - `waiting` (`integer`, at least 0): Contacts sitting at the step now.
  - `completed` (`integer`, at least 0): Contacts whose path ended at the step.
  - `exited` (`integer`, at least 0): Contacts that left early at the step.
  - `sent` (`integer`, at least 0): Emails the step sent.
  - `skipped` (`integer`, at least 0): Times the step did nothing, such as an address that cannot receive mail.
  - `failed` (`integer`, at least 0): Times the step failed for good.
  - `yes` (`integer`, at least 0): Contacts a branch sent down yes.
  - `no` (`integer`, at least 0): Contacts a branch sent down no.
  - `delivered` (`integer`, at least 0): Emails of the step that were delivered.
  - `opened` (`integer`, at least 0): Emails of the step that were opened.
  - `clicked` (`integer`, at least 0): Emails of the step with a click.
  - `bounced` (`integer`, at least 0): Emails of the step that bounced.
  - `complained` (`integer`, at least 0): Emails of the step marked as spam.
  - `unsubscribed` (`integer`, at least 0): Contacts that unsubscribed from an email of the step.
- `series` (`object[]`, required): One point for each UTC day in the window on which something happened, oldest first.
  - `day` (`string`, format `date-time`): The start of the day, in UTC.
  - `entered` (`integer`, at least 0): Contacts that entered that day.
  - `completed` (`integer`, at least 0): Contacts that completed that day.
  - `exited` (`integer`, at least 0): Contacts that left early that day.
  - `sent` (`integer`, at least 0): Emails sent that day.

#### `AutomationSummary`

`object`

- `object` (`string`, required, one of `"automation"`)
- `id` (`string`, required): The id of the automation. It never changes.
- `name` (`string`, required): What the workspace calls it. Contacts never see it.
- `description` (`string`, required, nullable): A note for the workspace.
- `status` (`string`, required, one of `"draft"`, `"live"`, `"paused"`, `"archived"`): `draft` until the first publish. `live` takes contacts in and moves them along. `paused` takes nobody in and holds everyone where they are. `archived` is retired for good and everyone in it has left.
- `triggerKind` (`string`, required, nullable, one of `"audience_joined"`, `"form_submitted"`, `"event"`, `"date"`, `"manual"`): What starts the draft, or null while it has no trigger.
- `stepCount` (`integer`, required, at least 0): How many steps the draft holds.
- `emailCount` (`integer`, required, at least 0): How many of those steps send an email.
- `hasUnpublishedChanges` (`boolean`, required): True when the draft differs from the version that is running.
- `publishedVersion` (`integer`, required, nullable): The number of the version that is running. Null until the first publish.
- `pausedReason` (`string`, required, nullable, one of `"manual"`, `"sender_refused"`, `"key_revoked"`, `"template_unavailable"`, `"audience_missing"`, `"plan_limit"`): Why it is paused. `manual` is a person. The others are OpenEmail stopping it: `sender_refused` when its from address can no longer send, `key_revoked` when the API key that published it was revoked, expired or turned off, `template_unavailable` when one of its templates can no longer be sent, `audience_missing` when an audience it depends on is gone, and `plan_limit` when the plan no longer covers this many live automations. Null while it is not paused.
- `lastError` (`string`, required, nullable): What went wrong the last time OpenEmail paused it, in a sentence. Null otherwise.
- `counts` (`object`, required): Contacts, counted at the moment of the read.
  - `active` (`integer`, required, at least 0): In the automation now.
  - `completed` (`integer`, required, at least 0): Reached the end of a path, in all its time.
  - `exited` (`integer`, required, at least 0): Left before the end, in all its time.
- `createdBy` (`string`, required, nullable): The user id of the person who made it. Null when that account is gone.
- `publishedAt` (`string`, required, nullable, format `date-time`): The last publish. Null until the first.
- `pausedAt` (`string`, required, nullable, format `date-time`): When it was paused. Null while it is not.
- `archivedAt` (`string`, required, nullable, format `date-time`): When it was archived. Null otherwise.
- `createdAt` (`string`, required, format `date-time`): When it was made.
- `updatedAt` (`string`, required, format `date-time`): The last save. Send it back as `expectedUpdatedAt` so a change never overwrites one you have not seen.

#### `AutomationTest`

`object`

- `object` (`string`, required, one of `"automation_test"`)
- `automationId` (`string`, required)
- `emailId` (`string`, required): The test email, as `GET /emails/{id}` returns it.
- `to` (`string`, required, format `email`): Where it went.
- `stepKey` (`string`, required): The step that was sent.

#### `AutomationVersion`

`object`

- `object` (`string`, required, one of `"automation_version"`)
- `id` (`string`, required)
- `automationId` (`string`, required)
- `version` (`integer`, required, at least 1): Counts up from 1 with each publish that changed the definition.
- `definition` (`object`, required): What starts an automation and what it then does. Paths never join or loop: every step is reached from exactly one place, and a definition holds at most 50 steps.
  - `trigger` (`object`, required, nullable): What puts a contact into the automation. Null in a draft that has none yet, and a draft cannot be published without one.
    - `kind` (`string`, required, one of `"audience_joined"`)
    - `audienceId` (`string`, required, up to 64 characters)
    - `includeImported` (`boolean`, required)
  - `entry` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`): The `key` of the first step, or null while the draft has no steps.
  - `steps` (`object[]`, required, up to 50 items): Every step, in any order. Each has a unique `key`, and names the step after it by key in `next`, or in `yes` and `no` for a branch. Null ends the path there.
    - `key` (`string`, required, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `name` (`string`, up to 80 characters)
    - `kind` (`string`, required, one of `"send_email"`)
    - `next` (`string`, required, nullable, pattern `^[a-z][a-z0-9]{2,23}$`)
    - `templateId` (`string`, required, up to 64 characters)
    - `templateVersion` (`integer`, required, nullable, at least 1)
    - `from` (`object`, required)
      - `email` (`string`, required, up to 320 characters)
      - `name` (`string`, up to 120 characters)
    - `replyTo` (`string`, required, nullable, up to 320 characters)
    - `subject` (`string`, required, nullable, up to 300 characters)
    - `props` (`Record<string, object>`, required)
- `current` (`boolean`, required): True for the version that is running.
- `createdBy` (`string`, required, nullable): The user id of whoever published it.
- `createdAt` (`string`, required, format `date-time`): When it was published.

#### `AutomationVersionList`

`object`

- `object` (`string`, one of `"list"`)
- `data` (`AutomationVersion[]`)

#### `DeletedAutomation`

`object`

- `object` (`string`, required, one of `"automation"`)
- `id` (`string`, required)
- `deleted` (`boolean`, required, one of `true`)
