---
title: "$client->automations"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://openemail.uk/docs/php/reference/automations"
area: "PHP"
category: "Reference"
---

# $client->automations

Every method in this namespace: its signature, its parameters, what it returns and an example.

## Methods

Emails and other steps that run by themselves for each contact, such as a welcome series or a birthday note: build a draft from a starter or your own trigger and steps, publish, pause, resume and archive it, send a test, read its versions and statistics, and enroll contacts or take them out.

### `automations->list`

List one page of the automations in the workspace

```php
list(
    ?string $status = null,
    ?int $limit = null,
    ?string $cursor = null,
    ?string $apiKey = null,
): Page
```

Returns one page of the automations the caller can reach, the most recently saved first, without their definitions or settings. Paging is keyset: `limit:` takes 1 to 100 and defaults to 50, and `nextCursor` goes back as `cursor:` while `hasMore` is true. `listAll` and `iterate` do that walk for you.

Each automation carries its `status`, `triggerKind`, how many steps and emails the draft holds, `hasUnpublishedChanges`, `publishedVersion` and `counts`: the contacts in it now, and the ones that completed or left in all its time, counted at the moment of the read. A paused automation says why in `pausedReason`.

Read one with `get` for the draft `definition`, the `published` one, the `settings` and the `problems` that would stop a publish.

Scopes: `automations:read`.

**Parameters**

- `status` (`string`): Only automations in this state: `draft`, `live`, `paused` or `archived`.
- `limit` (`int`): Rows per page, a whole number from 1 to 100. The server defaults to 50.
- `cursor` (`string`): The `nextCursor` from the previous page, passed back exactly as it came. One that names no automation is a 400 `invalid_cursor`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

A `Page` of arrays with `items`, `hasMore` and `nextCursor`. Each item has `id`, `name`, `description`, `status`, `triggerKind`, `stepCount`, `emailCount`, `hasUnpublishedChanges`, `publishedVersion`, `pausedReason`, `lastError`, `counts`, `createdBy`, `publishedAt`, `pausedAt`, `archivedAt`, `createdAt` and `updatedAt`.

**Example**

```php
use OpenEmail\Constants\AutomationStatuses;

$page = $client->automations->list(status: AutomationStatuses::LIVE);

foreach ($page as $automation) {
    echo $automation['name'], ' ', $automation['triggerKind'], ', ', $automation['counts']['active'], ' in it now', PHP_EOL;
}
```

**Notes**

- An app a member connected lists only the automations that member made. An API key and an app the owner connected list every automation in the workspace.
- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /automations`](https://openemail.uk/docs/api/reference/automations#get-automations); TypeScript [`automations.list()`](https://openemail.uk/docs/sdk/reference/automations#list); Python [`automations.list()`](https://openemail.uk/docs/python/reference/automations#list); Ruby [`automations.list`](https://openemail.uk/docs/ruby/reference/automations#list); Go [`Automations.List`](https://openemail.uk/docs/go/reference/automations#list); Java [`automations().list`](https://openemail.uk/docs/java/reference/automations#list); C# [`Automations.ListAsync`](https://openemail.uk/docs/csharp/reference/automations#list); CLI [`openemail automations list`](https://openemail.uk/docs/cli/reference/automations#automations-list).

### `automations->listAll`

Collect every automation you can reach into one array

```php
listAll(
    ?string $status = null,
    ?int $limit = null,
    ?string $cursor = null,
    ?string $apiKey = null,
): array
```

Follows `nextCursor` from page to page and returns every automation the caller can reach, the most recently saved first, in the shape `list` returns. `limit:` sets the page size of each request, not the total.

Scopes: `automations:read`.

**Parameters**

- `status` (`string`): Only automations in this state: `draft`, `live`, `paused` or `archived`.
- `limit` (`int`): Page size per request, from 1 to 100, defaulting to 50 on the server.
- `cursor` (`string`): A `nextCursor` from an earlier page to start after.
- `apiKey` (`string`): Overrides the client's API key for every page of this walk.

**Returns**

A list of arrays holding every automation across all pages, each shaped like an item of `list`.

**Example**

```php
use OpenEmail\Constants\AutomationStatuses;

$automations = $client->automations->listAll();

foreach ($automations as $automation) {
    if ($automation['status'] === AutomationStatuses::PAUSED) {
        echo $automation['name'], ' ', $automation['pausedReason'], PHP_EOL;
    }
}
```

**Notes**

- A failure on any page throws out of the whole call, and the automations already fetched are discarded.

Also available in: API [`GET /automations`](https://openemail.uk/docs/api/reference/automations#get-automations); TypeScript [`automations.listAll()`](https://openemail.uk/docs/sdk/reference/automations#listAll); Python [`automations.list_all()`](https://openemail.uk/docs/python/reference/automations#listAll); Ruby [`automations.list_all`](https://openemail.uk/docs/ruby/reference/automations#listAll); Go [`Automations.ListAll`](https://openemail.uk/docs/go/reference/automations#listAll); Java [`automations().listAll`](https://openemail.uk/docs/java/reference/automations#listAll); C# [`Automations.ListAllAsync`](https://openemail.uk/docs/csharp/reference/automations#listAll).

### `automations->iterate`

Stream the automations you can reach one at a time

```php
iterate(
    ?string $status = null,
    ?int $limit = null,
    ?string $cursor = null,
    ?string $apiKey = null,
): Generator
```

Returns a `Generator` that yields automations one by one, the most recently saved first, and requests the next page only once the current one is drained. Breaking out of the loop stops the requests.

Scopes: `automations:read`.

**Parameters**

- `status` (`string`): Only automations in this state: `draft`, `live`, `paused` or `archived`.
- `limit` (`int`): Page size per request, from 1 to 100, defaulting to 50 on the server.
- `cursor` (`string`): A `nextCursor` from an earlier page to start after.
- `apiKey` (`string`): Overrides the client's API key for every page of this walk.

**Returns**

A `Generator` that yields one automation array per step.

**Example**

```php
foreach ($client->automations->iterate() as $automation) {
    if ($automation['hasUnpublishedChanges']) {
        echo 'Unpublished edits: ', $automation['name'], PHP_EOL;
    }
}
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.

Also available in: API [`GET /automations`](https://openemail.uk/docs/api/reference/automations#get-automations); TypeScript [`automations.iterate()`](https://openemail.uk/docs/sdk/reference/automations#iterate); Python [`automations.iterate()`](https://openemail.uk/docs/python/reference/automations#iterate); Ruby [`automations.iterate`](https://openemail.uk/docs/ruby/reference/automations#iterate); Go [`Automations.Iterate`](https://openemail.uk/docs/go/reference/automations#iterate); Java [`automations().iterate`](https://openemail.uk/docs/java/reference/automations#iterate); C# [`Automations.IterateAsync`](https://openemail.uk/docs/csharp/reference/automations#iterate).

### `automations->create`

Create an automation as a draft

```php
create(array $body, ?string $apiKey = null): array
```

Makes a draft automation and returns it whole, in the shape `get` returns. Its trigger and steps come from `definition` when you send one, from the starter named in `starter` when you do not, and otherwise the draft starts empty. `settings` changes any of the defaults.

A draft may be incomplete: `problems` in the answer lists what is still missing, such as a template or a from address a starter leaves for you. Nothing runs until `publish`, which needs a complete draft and `emails:send`.

Scopes: `automations:write`.

**Parameters**

- `name` (`string`, required): What the workspace calls the automation, trimmed, 1 to 120 characters. Contacts never see it, and it need not be unique.
- `description` (`string|null`): A note for the workspace, at most 500 characters. Contacts never see it.
- `starter` (`string`): Begin from a starter, by the `slug` `listStarters` returns: `welcome-series`, `trial-follow-up`, `birthday` or `win-back`. Ignored when `definition` is sent. An unknown slug is 422 `invalid_automation`.
- `definition` (`array`): The trigger and the steps, in the shape `get` returns as `definition`. `trigger` says what starts the automation: `audience_joined` with `audienceId` and `includeImported`, `form_submitted` with `formId`, `event` with `eventName` and `filters`, `date` with `field`, `audienceId` and `offsetDays`, or `manual`. `entry` is the key of the first step, and `steps` holds every step: `send_email`, `wait`, `branch`, `add_to_audience`, `remove_from_audience`, `update_field`, `webhook` or `exit`. Each step has a unique `key` of 3 to 24 lowercase letters and digits starting with a letter, and names the next step by key in `next`, or in `yes` and `no` for a branch. Null ends the path. Paths never join or loop, and a definition holds at most 50 steps. Leave it out to use `starter` or an empty draft.
- `settings.timezone` (`string`): The IANA time zone the sending window and waits until a day and time are read in, such as `Europe/London`. Defaults to `UTC`.
- `settings.sendWindow` (`array|null`): When emails may go out: `days` of the week, where 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, the default, sends at any time.
- `settings.reentryDays` (`int|null`): How many days after a contact finishes before they may enter again, from 1 to 3650. Null, the default, lets each contact through once only.
- `settings.exitOnLeave` (`bool`): Take a contact out when they leave the audience that started the automation. Defaults to true.
- `settings.listAudienceId` (`string|null`): The audience an unsubscribe from one of these emails is recorded in. Null, the default, uses the audience that starts the automation, or the built-in audience of every contact when no audience starts it. An id that is not an audience of the workspace is 422 `invalid_automation`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array, the automation as `get` returns it: the fields of a `list` item with `status` `draft`, plus `definition`, `published`, which is null, `settings` and `problems`.

**Example**

```php
$automation = $client->automations->create([
    'name' => 'Welcome series',
    'starter' => 'welcome-series',
    'settings' => ['timezone' => 'Europe/London'],
]);

foreach ($automation['problems'] as $problem) {
    echo $problem['path'], ': ', $problem['message'], PHP_EOL;
}
```

**Notes**

- A value of the wrong type or length is 422 `invalid_parameter` with `param` naming it, and a key the body does not take is 422 `unknown_parameter`. A definition that breaks a rule of its structure, such as two steps with one key or two paths into one step, is 422 `invalid_automation`, and `problems` on the error lists each one.
- A workspace holds a limited number of automations, drafts included, and support can raise that for a workspace that needs more. The next one past the limit is 422 `workspace_limit_reached`.
- Not retried automatically, and nothing deduplicates by name, so a retry after a lost response can leave two drafts. List them and delete the spare.

Also available in: API [`POST /automations`](https://openemail.uk/docs/api/reference/automations#post-automations); 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); 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).

### `automations->listStarters`

List the ready-made automations to start from

```php
listStarters(?string $apiKey = null): array
```

Returns the starting points the app offers when somebody makes an automation: a welcome series, a trial follow-up, a birthday note and a win-back. Each comes with its whole `definition`, so you can read it, change it and send it to `create`, or pass its `slug` to `create` as `starter`.

A starter leaves the audience, the templates and the from address as empty strings for you to fill in. The list is the same for every workspace and is not paginated.

Scopes: `automations:read`.

**Parameters**

- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

A list of arrays, each with `slug`, `name`, `description` and `definition`.

**Example**

```php
foreach ($client->automations->listStarters() as $starter) {
    echo $starter['slug'], ': ', $starter['description'], PHP_EOL;
}
```

**Notes**

- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /automations/starters`](https://openemail.uk/docs/api/reference/automations#get-automations-starters); 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); 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).

### `automations->get`

Retrieve an automation with its definition and settings

```php
get(string $id, ?string $apiKey = null): array
```

Returns the whole automation: the draft `definition` you edit, `published`, the definition of the version that is running, `settings`, fresh `counts` and `problems`.

`problems` is what is wrong with the draft at the moment of the read, each with a `code`, the `path` of the field, the `stepKey`, a `message` and `blocking`, which is false for a warning. An empty list means the draft is ready, though `publish` checks more: that every template can be sent and that the caller may send from every from address.

Scopes: `automations:read`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array with `id`, `name`, `description`, `status`, `triggerKind`, `stepCount`, `emailCount`, `hasUnpublishedChanges`, `publishedVersion`, `pausedReason`, `lastError`, `counts`, `createdBy`, `publishedAt`, `pausedAt`, `archivedAt`, `createdAt` and `updatedAt`, plus `definition`, `published`, `settings` and `problems`.

**Example**

```php
$automation = $client->automations->get('aut_5c1e9a7b3d2f48e6a0b4c7d1');

echo $automation['status'], ', ', count($automation['definition']['steps']), ' steps', PHP_EOL;

foreach ($automation['problems'] as $problem) {
    if ($problem['blocking']) {
        echo $problem['message'], PHP_EOL;
    }
}
```

**Notes**

- An id that names no automation the caller can reach is 404 `automation_not_found`, whether it does not exist or belongs to another workspace.
- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /automations/{id}`](https://openemail.uk/docs/api/reference/automations#get-automations-id); 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); 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).

### `automations->update`

Change the draft, the settings or the name of an automation

```php
update(string $id, array $patch, ?string $apiKey = null): array
```

A partial update that returns the automation whole. `definition` replaces the draft, and a live automation keeps running its published version until you `publish` again, so an edit never changes the path of a contact who is halfway through. `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, the call is refused with 409 `version_conflict` instead of writing over their change.

Scopes: `automations:write`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `name` (`string`): New name, trimmed, 1 to 120 characters.
- `description` (`string|null`): New note, at most 500 characters. Null clears it.
- `definition` (`array`): The new draft, whole. The trigger and the steps, in the shape `get` returns as `definition`. `trigger` says what starts the automation: `audience_joined` with `audienceId` and `includeImported`, `form_submitted` with `formId`, `event` with `eventName` and `filters`, `date` with `field`, `audienceId` and `offsetDays`, or `manual`. `entry` is the key of the first step, and `steps` holds every step: `send_email`, `wait`, `branch`, `add_to_audience`, `remove_from_audience`, `update_field`, `webhook` or `exit`. Each step has a unique `key` of 3 to 24 lowercase letters and digits starting with a letter, and names the next step by key in `next`, or in `yes` and `no` for a branch. Null ends the path. Paths never join or loop, and a definition holds at most 50 steps.
- `settings.timezone` (`string`): The IANA time zone the sending window and waits until a day and time are read in, such as `Europe/London`. Defaults to `UTC`.
- `settings.sendWindow` (`array|null`): When emails may go out: `days` of the week, where 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, the default, sends at any time.
- `settings.reentryDays` (`int|null`): How many days after a contact finishes before they may enter again, from 1 to 3650. Null, the default, lets each contact through once only.
- `settings.exitOnLeave` (`bool`): Take a contact out when they leave the audience that started the automation. Defaults to true.
- `settings.listAudienceId` (`string|null`): The audience an unsubscribe from one of these emails is recorded in. Null, the default, uses the audience that starts the automation, or the built-in audience of every contact when no audience starts it. An id that is not an audience of the workspace is 422 `invalid_automation`.
- `expectedUpdatedAt` (`string`): The `updatedAt` you read, as an ISO 8601 instant. When the automation was saved since, the call is refused with 409 `version_conflict` and nothing is written.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array, the automation as `get` returns it after the save, with a new `updatedAt` and, when the draft changed, `hasUnpublishedChanges` true on a published automation.

**Example**

```php
$current = $client->automations->get('aut_5c1e9a7b3d2f48e6a0b4c7d1');

$saved = $client->automations->update($current['id'], [
    'settings' => ['sendWindow' => ['days' => [1, 2, 3, 4, 5], 'startMinute' => 540, 'endMinute' => 1020]],
    'expectedUpdatedAt' => $current['updatedAt'],
]);

echo 'Saved at ', $saved['updatedAt'], PHP_EOL;
```

**Notes**

- An archived automation cannot be changed and answers 409 `automation_archived`.
- A value of the wrong type or length is 422 `invalid_parameter`, a key the patch does not take is 422 `unknown_parameter`, and a definition that breaks a rule of its structure is 422 `invalid_automation` with `problems` on the error.
- Retried automatically on network failure and retryable statuses, since saving the same patch twice leaves the same automation. With `expectedUpdatedAt`, a retry after a lost response answers 409 `version_conflict`: read the automation to see that your change is there.

Also available in: API [`PATCH /automations/{id}`](https://openemail.uk/docs/api/reference/automations#patch-automations-id); 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); 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).

### `automations->delete`

Delete an automation with its versions and history

```php
delete(string $id, ?string $apiKey = null): array
```

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 `emails->list`.

There is no undo. To retire an automation and keep its history, use `archive`.

Scopes: `automations:write`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array with `object` set to `automation`, `id` and `deleted` set to true.

**Example**

```php
$removed = $client->automations->delete('aut_5c1e9a7b3d2f48e6a0b4c7d1');

echo $removed['id'], $removed['deleted'] ? ' is gone' : ' is still there', PHP_EOL;
```

**Notes**

- An OAuth access token needs a verification code for this call, and is refused with 403 `step_up_required` until the app has verified one in the last 60 minutes. `isStepUpRequired()` on the error says so. An API key is never asked for a code.
- The SDK does not retry a delete. A 404 on your own second attempt after a lost response means the first one worked.

Also available in: API [`DELETE /automations/{id}`](https://openemail.uk/docs/api/reference/automations#delete-automations-id); 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); 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).

### `automations->publish`

Publish the draft and turn the automation on

```php
publish(string $id, ?string $apiKey = null): array
```

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. It takes 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. Anything else is 422 `invalid_automation`, and `problems` on the error lists every problem that blocks it.

The automation sends as whoever published it. When that API key is revoked, expires or is turned off, the automation pauses itself with `pausedReason` `key_revoked`.

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

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array, the automation as `get` returns it, with `status` `live`, a new `publishedAt`, the `publishedVersion` that is now running and `hasUnpublishedChanges` false.

**Example**

```php
$automation = $client->automations->publish('aut_5c1e9a7b3d2f48e6a0b4c7d1');

echo $automation['status'], ', version ', $automation['publishedVersion'], ' since ', $automation['publishedAt'], PHP_EOL;
```

**Notes**

- A plan covers a number of live automations: 1 on Free, 10 on Starter and 50 on Business, with no limit on Enterprise. One more is 403 `automation_limit_reached`. Pause one, or upgrade.
- An archived automation answers 409 `automation_archived`.
- Publishing a draft that has not changed since the last publish keeps the same version number.
- Retried automatically on network failure and retryable statuses, since publishing the same draft twice leaves the same live version.

Also available in: API [`POST /automations/{id}/publish`](https://openemail.uk/docs/api/reference/automations#post-automations-id-publish); 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); 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).

### `automations->pause`

Stop a live automation without losing anybody

```php
pause(string $id, ?string $apiKey = null): array
```

Stops a live automation. Nobody new enters, and everyone in it stays where they are and moves on when it is resumed. The `automation.paused` webhook event fires. Pausing a paused automation changes nothing. It takes no body.

Scopes: `automations:write`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array, the automation as `get` returns it, with `status` `paused`, `pausedReason` `manual` and `pausedAt` set.

**Example**

```php
$automation = $client->automations->pause('aut_5c1e9a7b3d2f48e6a0b4c7d1');

echo $automation['status'], ' (', $automation['pausedReason'], '), ', $automation['counts']['active'], ' held', PHP_EOL;
```

**Notes**

- A draft that was never published answers 409 `automation_not_published`, and an archived automation 409 `automation_archived`.
- Retried automatically on network failure and retryable statuses, since pausing twice leaves the same paused automation.

Also available in: API [`POST /automations/{id}/pause`](https://openemail.uk/docs/api/reference/automations#post-automations-id-pause); 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); 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).

### `automations->resume`

Turn a paused automation back on

```php
resume(string $id, ?string $apiKey = null): array
```

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. Resuming a live automation changes nothing. It takes no body.

The published version is checked again first, exactly as `publish` checks a draft. An automation OpenEmail paused, because its from address, its templates or an audience went away, stays paused with 422 `invalid_automation` until what stopped it is fixed.

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

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array, the automation as `get` returns it, with `status` `live` and `pausedReason`, `pausedAt` and `lastError` cleared.

**Example**

```php
$automation = $client->automations->resume('aut_5c1e9a7b3d2f48e6a0b4c7d1');

echo $automation['status'], ', version ', $automation['publishedVersion'], PHP_EOL;
```

**Notes**

- An automation with no published version answers 409 `automation_not_published`, an archived one 409 `automation_archived`, and a plan that covers no more live automations 403 `automation_limit_reached`.
- Retried automatically on network failure and retryable statuses, since resuming twice leaves the same live automation.

Also available in: API [`POST /automations/{id}/resume`](https://openemail.uk/docs/api/reference/automations#post-automations-id-resume); 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); 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).

### `automations->archive`

Retire an automation for good and keep its history

```php
archive(string $id, ?string $apiKey = null): array
```

Retires an automation. 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. It takes no body.

An automation with many contacts in it empties in the background over the next minutes. To start again from its steps, use `duplicate`.

Scopes: `automations:write`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array, the automation as `get` returns it, with `status` `archived` and `archivedAt` set.

**Example**

```php
$automation = $client->automations->archive('aut_5c1e9a7b3d2f48e6a0b4c7d1');

echo $automation['status'], ' at ', $automation['archivedAt'], PHP_EOL;
```

**Notes**

- There is no way back from archived. To stop an automation for a while, use `pause`.
- Retried automatically on network failure and retryable statuses, since archiving twice leaves the same archived automation.

Also available in: API [`POST /automations/{id}/archive`](https://openemail.uk/docs/api/reference/automations#post-automations-id-archive); 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); 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).

### `automations->duplicate`

Copy an automation into a new draft

```php
duplicate(string $id, ?string $apiKey = null): array
```

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, and it works on an archived automation too. It takes no body.

Scopes: `automations:write`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array, the copy as `get` returns it, with its own `id` and `status` `draft`.

**Example**

```php
$copy = $client->automations->duplicate('aut_5c1e9a7b3d2f48e6a0b4c7d1');

echo $copy['id'], ' ', $copy['name'], ' is a ', $copy['status'], PHP_EOL;
```

**Notes**

- A workspace that already holds the most automations it may answers 422 `workspace_limit_reached`.
- Not retried automatically, so a retry after a lost response can leave two copies. List them and delete the spare.

Also available in: API [`POST /automations/{id}/duplicate`](https://openemail.uk/docs/api/reference/automations#post-automations-id-duplicate); 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); 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).

### `automations->sendTest`

Send yourself the email of one step

```php
sendTest(string $id, array $body, ?string $apiKey = null): array
```

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.

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

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `stepKey` (`string`, required): The `key` of the email step to send, from the draft `definition`.
- `to` (`string`): Where the test goes. Left out, it goes to the account email of the person the key or app acts for: the workspace owner for an API key.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array with `automationId`, `emailId`, the email as `emails->get` returns it, `to` and `stepKey`.

**Example**

```php
$test = $client->automations->sendTest('aut_5c1e9a7b3d2f48e6a0b4c7d1', [
    'stepKey' => 'welcome',
    'to' => 'ada@example.com',
]);

echo $test['emailId'], ' went to ', $test['to'], PHP_EOL;
```

**Notes**

- A `stepKey` that names no step, a step that is not an email, or a template that cannot be sent is 422 `invalid_automation` with `param` `stepKey`.
- The from address of the step has to be one the caller may send as, or the call is refused with 403 `from_address_forbidden`. A domain that cannot send yet is 409 `domain_not_sendable`, and a plan with no sends left is 429 `send_quota_exceeded`.
- Not retried automatically, because a retry after a lost response would send the test twice.

Also available in: API [`POST /automations/{id}/test`](https://openemail.uk/docs/api/reference/automations#post-automations-id-test); 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); 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).

### `automations->listVersions`

List the published versions of an automation

```php
listVersions(string $id, ?string $apiKey = null): array
```

Returns every published version, the newest first, each with its whole definition. `current` marks the one that is running. A publish that changed the definition adds a version, and each contact stays on the version they entered on. A draft that was never published has none. The list is not paginated.

Scopes: `automations:read`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

A list of arrays, each with `id`, `automationId`, `version`, `definition`, `current`, `createdBy` and `createdAt`.

**Example**

```php
foreach ($client->automations->listVersions('aut_5c1e9a7b3d2f48e6a0b4c7d1') as $version) {
    echo $version['version'], $version['current'] ? ' (running)' : '', ' ', $version['createdAt'], PHP_EOL;
}
```

**Notes**

- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /automations/{id}/versions`](https://openemail.uk/docs/api/reference/automations#get-automations-id-versions); 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); 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).

### `automations->restoreVersion`

Copy an earlier version back into the draft

```php
restoreVersion(string $id, int $version, ?string $apiKey = null): array
```

Copies the definition of an earlier version into the draft, replacing what the draft holds. Nothing that is running changes until you `publish`. It takes no body.

Scopes: `automations:write`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `version` (`int`, required): The number of the version, from `listVersions`. A whole number from 1 up.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array, the automation as `get` returns it, with the version in `definition`, and `hasUnpublishedChanges` true when it differs from the one that is running.

**Example**

```php
$automation = $client->automations->restoreVersion('aut_5c1e9a7b3d2f48e6a0b4c7d1', 2);

echo $automation['hasUnpublishedChanges'] ? 'The draft differs from what is running' : 'Nothing changed', PHP_EOL;
```

**Notes**

- A version the automation does not have is 404 `automation_not_found` with `param` `version`, and an archived automation is 409 `automation_archived`.
- Retried automatically on network failure and retryable statuses, since restoring the same version twice leaves the same draft.

Also available in: API [`POST /automations/{id}/versions/{version}/restore`](https://openemail.uk/docs/api/reference/automations#post-automations-id-versions-version-restore); 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); 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).

### `automations->stats`

Read how an automation has performed

```php
stats(
    string $id,
    DateTimeInterface|string|null $since = null,
    DateTimeInterface|string|null $until = null,
    ?string $apiKey = null,
): array
```

Returns the numbers of an automation over a window: `totals` for the whole automation, `steps` with the same numbers step by step, and `series` with a point for each UTC day on which something happened. The window defaults to the last 30 days and reaches back at most 366 days before `until:`.

The steps are those of the published version, or of the draft when nothing is published. `active` in `totals` and the `waiting` of each step are counted at the moment of the read, whatever the window. Opens are a floor, because many mail apps hide them.

Scopes: `automations:read`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `since` (`string|DateTimeInterface`): Where the window starts, as a `DateTimeInterface` or an ISO 8601 string. Left out, 30 days before `until:`.
- `until` (`string|DateTimeInterface`): Where the window ends, as a `DateTimeInterface` or an ISO 8601 string. Left out, now. It has to be later than `since:`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array with `automationId`, `since`, `until`, `totals` (`entered`, `active`, `completed`, `exited`, `sent`, `delivered`, `opened`, `clicked`, `bounced`, `complained` and `unsubscribed`), `steps` and `series`.

**Example**

```php
$stats = $client->automations->stats('aut_5c1e9a7b3d2f48e6a0b4c7d1', since: '2026-09-01T00:00:00Z');

echo $stats['totals']['entered'], ' entered, ', $stats['totals']['sent'], ' sent, ', $stats['totals']['clicked'], ' clicked', PHP_EOL;

foreach ($stats['steps'] as $step) {
    echo $step['stepKey'], ' ', $step['kind'], ': ', $step['entered'], ' entered, ', $step['waiting'], ' waiting', PHP_EOL;
}
```

**Notes**

- A time that is not an ISO 8601 instant, or an `until:` that is not after `since:`, is 422 `invalid_parameter`.
- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /automations/{id}/stats`](https://openemail.uk/docs/api/reference/automations#get-automations-id-stats); 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); 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).

### `automations->listEnrollments`

List one page of the contacts in an automation

```php
listEnrollments(
    string $id,
    ?string $status = null,
    ?string $stepKey = null,
    ?string $q = null,
    ?int $limit = null,
    ?string $cursor = null,
    ?string $apiKey = null,
): Page
```

Returns one page of everyone who is in the automation or has been, the most recent entry first. Each enrollment says who the `contact` is, the `stepKey` they are at, whether they are `waiting` and for which event, what holds a step that is due in `heldFor`, when they move next in `nextRunAt`, and how it ended in `exitReason`.

`limit:` takes 1 to 200 and defaults to 50. Pass `nextCursor` back as `cursor:`, with the same filters, while `hasMore` is true. `listAllEnrollments` and `iterateEnrollments` do that walk for you.

Scopes: `automations:read`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `status` (`string`): Only enrollments in this state: `active`, `completed` or `exited`.
- `stepKey` (`string`): Only contacts at the step with this `key`.
- `q` (`string`): Only contacts whose address or name contains this text, compared without case, up to 200 characters.
- `limit` (`int`): Rows per page, a whole number from 1 to 200. The server defaults to 50.
- `cursor` (`string`): The `nextCursor` from the previous page, passed back exactly as it came. One that names no enrollment of this automation is a 400 `invalid_cursor`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

A `Page` of arrays with `items`, `hasMore` and `nextCursor`. Each item has `id`, `automationId`, `version`, `contact`, `status`, `source`, `stepKey`, `waiting`, `waitingForEvent`, `heldFor`, `nextRunAt`, `exitReason`, `lastError`, `startedAt`, `finishedAt` and `updatedAt`.

**Example**

```php
use OpenEmail\Constants\AutomationEnrollmentStatuses;

$page = $client->automations->listEnrollments('aut_5c1e9a7b3d2f48e6a0b4c7d1', status: AutomationEnrollmentStatuses::ACTIVE);

foreach ($page as $enrollment) {
    echo $enrollment['contact']['email'], ' at ', $enrollment['stepKey'], ', next ', $enrollment['nextRunAt'], PHP_EOL;
}
```

**Notes**

- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /automations/{id}/enrollments`](https://openemail.uk/docs/api/reference/automations#get-automations-id-enrollments); TypeScript [`automations.listEnrollments()`](https://openemail.uk/docs/sdk/reference/automations#listEnrollments); Python [`automations.list_enrollments()`](https://openemail.uk/docs/python/reference/automations#listEnrollments); Ruby [`automations.list_enrollments`](https://openemail.uk/docs/ruby/reference/automations#listEnrollments); Go [`Automations.ListEnrollments`](https://openemail.uk/docs/go/reference/automations#listEnrollments); Java [`automations().listEnrollments`](https://openemail.uk/docs/java/reference/automations#listEnrollments); C# [`Automations.ListEnrollmentsAsync`](https://openemail.uk/docs/csharp/reference/automations#listEnrollments); CLI [`openemail automations list-enrollments`](https://openemail.uk/docs/cli/reference/automations#automations-list-enrollments).

### `automations->listAllEnrollments`

Collect every enrollment of an automation into one array

```php
listAllEnrollments(
    string $id,
    ?string $status = null,
    ?string $stepKey = null,
    ?string $q = null,
    ?int $limit = null,
    ?string $cursor = null,
    ?string $apiKey = null,
): array
```

Follows `nextCursor` from page to page and returns every enrollment that matches, the most recent entry first, in the shape `listEnrollments` returns. `limit:` sets the page size of each request, not the total. An automation can hold a great many enrollments, so prefer `iterateEnrollments` when you do not need them all in memory.

Scopes: `automations:read`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `status` (`string`): Only enrollments in this state: `active`, `completed` or `exited`.
- `stepKey` (`string`): Only contacts at the step with this `key`.
- `q` (`string`): Only contacts whose address or name contains this text, compared without case, up to 200 characters.
- `limit` (`int`): Page size per request, from 1 to 200, defaulting to 50 on the server.
- `cursor` (`string`): A `nextCursor` from an earlier page to start after.
- `apiKey` (`string`): Overrides the client's API key for every page of this walk.

**Returns**

A list of arrays holding every matching enrollment across all pages, each shaped like an item of `listEnrollments`.

**Example**

```php
use OpenEmail\Constants\AutomationEnrollmentStatuses;
use OpenEmail\Constants\AutomationExitReasons;

$exited = $client->automations->listAllEnrollments('aut_5c1e9a7b3d2f48e6a0b4c7d1', status: AutomationEnrollmentStatuses::EXITED, limit: 200);

$unsubscribed = array_filter($exited, static fn(array $enrollment): bool => $enrollment['exitReason'] === AutomationExitReasons::UNSUBSCRIBED);

echo count($unsubscribed), ' of ', count($exited), ' left by unsubscribing', PHP_EOL;
```

**Notes**

- A failure on any page throws out of the whole call, and the enrollments already fetched are discarded.

Also available in: API [`GET /automations/{id}/enrollments`](https://openemail.uk/docs/api/reference/automations#get-automations-id-enrollments); TypeScript [`automations.listAllEnrollments()`](https://openemail.uk/docs/sdk/reference/automations#listAllEnrollments); Python [`automations.list_all_enrollments()`](https://openemail.uk/docs/python/reference/automations#listAllEnrollments); Ruby [`automations.list_all_enrollments`](https://openemail.uk/docs/ruby/reference/automations#listAllEnrollments); Go [`Automations.ListAllEnrollments`](https://openemail.uk/docs/go/reference/automations#listAllEnrollments); Java [`automations().listAllEnrollments`](https://openemail.uk/docs/java/reference/automations#listAllEnrollments); C# [`Automations.ListAllEnrollmentsAsync`](https://openemail.uk/docs/csharp/reference/automations#listAllEnrollments).

### `automations->iterateEnrollments`

Stream the enrollments of an automation one at a time

```php
iterateEnrollments(
    string $id,
    ?string $status = null,
    ?string $stepKey = null,
    ?string $q = null,
    ?int $limit = null,
    ?string $cursor = null,
    ?string $apiKey = null,
): Generator
```

Returns a `Generator` that yields enrollments one by one, the most recent entry first, and requests the next page only once the current one is drained. Breaking out of the loop stops the requests.

Scopes: `automations:read`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `status` (`string`): Only enrollments in this state: `active`, `completed` or `exited`.
- `stepKey` (`string`): Only contacts at the step with this `key`.
- `q` (`string`): Only contacts whose address or name contains this text, compared without case, up to 200 characters.
- `limit` (`int`): Page size per request, from 1 to 200, defaulting to 50 on the server.
- `cursor` (`string`): A `nextCursor` from an earlier page to start after.
- `apiKey` (`string`): Overrides the client's API key for every page of this walk.

**Returns**

A `Generator` that yields one enrollment array per step.

**Example**

```php
use OpenEmail\Constants\AutomationEnrollmentStatuses;

foreach ($client->automations->iterateEnrollments('aut_5c1e9a7b3d2f48e6a0b4c7d1', status: AutomationEnrollmentStatuses::ACTIVE) as $enrollment) {
    if ($enrollment['heldFor'] !== null) {
        echo $enrollment['contact']['email'], ' held for ', $enrollment['heldFor'], PHP_EOL;
    }
}
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.

Also available in: API [`GET /automations/{id}/enrollments`](https://openemail.uk/docs/api/reference/automations#get-automations-id-enrollments); TypeScript [`automations.iterateEnrollments()`](https://openemail.uk/docs/sdk/reference/automations#iterateEnrollments); Python [`automations.iterate_enrollments()`](https://openemail.uk/docs/python/reference/automations#iterateEnrollments); Ruby [`automations.iterate_enrollments`](https://openemail.uk/docs/ruby/reference/automations#iterateEnrollments); Go [`Automations.IterateEnrollments`](https://openemail.uk/docs/go/reference/automations#iterateEnrollments); Java [`automations().iterateEnrollments`](https://openemail.uk/docs/java/reference/automations#iterateEnrollments); C# [`Automations.IterateEnrollmentsAsync`](https://openemail.uk/docs/csharp/reference/automations#iterateEnrollments).

### `automations->getEnrollment`

Retrieve one contact's way through an automation

```php
getEnrollment(string $id, string $enrollmentId, ?string $apiKey = null): array
```

Returns one enrollment with `runs`: what each step did for the contact, oldest first, up to 200. A run has the `stepKey`, the `kind` of step, an `outcome` such as `sent`, `waited`, `yes`, `no`, `skipped` or `failed`, the `emailId` a send step produced and a `detail` that says why a step was skipped or failed.

Scopes: `automations:read`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `enrollmentId` (`string`, required): Enrollment id such as `aen_2b8d4f6a1c3e5079b6d8f0a2`, from `listEnrollments`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array with `id`, `automationId`, `version`, `contact`, `status`, `source`, `stepKey`, `waiting`, `waitingForEvent`, `heldFor`, `nextRunAt`, `exitReason`, `lastError`, `startedAt`, `finishedAt` and `updatedAt`, plus `runs`.

**Example**

```php
$enrollment = $client->automations->getEnrollment('aut_5c1e9a7b3d2f48e6a0b4c7d1', 'aen_2b8d4f6a1c3e5079b6d8f0a2');

foreach ($enrollment['runs'] as $run) {
    echo $run['createdAt'], ' ', $run['stepKey'], ' ', $run['outcome'], ' ', $run['detail'], PHP_EOL;
}
```

**Notes**

- An enrollment id that is not in this automation is 404 `automation_enrollment_not_found`.
- Read only, so the SDK retries it after a network failure like any other read.

Also available in: API [`GET /automations/{id}/enrollments/{enrollmentId}`](https://openemail.uk/docs/api/reference/automations#get-automations-id-enrollments-enrollmentid); 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); 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).

### `automations->enroll`

Put a contact into a live automation

```php
enroll(string $id, array $body, ?string $apiKey = null): array
```

Puts one contact into a live automation at its first step, whatever its trigger is. Name the contact with `email` or `contactId`, never both. The contact has to exist already: save one with `contacts->create` first. `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 the `reentryDays` of its settings. An address on the suppression list, or one that unsubscribed from the audience the automation sends through, is refused.

Scopes: `automations:write`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `email` (`string`): The contact, by email address. Send this or `contactId`.
- `contactId` (`string`): The contact, by id, as an event or another enrollment carries it. Send this or `email`.
- `data` (`array`): 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.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array, the enrollment shaped like an item of `listEnrollments`, with `status` `active`, `source` `api` and `nextRunAt` now. The first step runs within about a minute.

**Example**

```php
$enrollment = $client->automations->enroll('aut_5c1e9a7b3d2f48e6a0b4c7d1', [
    'email' => 'ada@example.com',
    'data' => ['plan' => 'team'],
]);

echo $enrollment['id'], ' is ', $enrollment['status'], ', first step at ', $enrollment['nextRunAt'], PHP_EOL;
```

**Notes**

- An automation that is a draft, paused or archived answers 409 `automation_not_live`. A contact who is in it, or finished it too recently, answers 409 `already_enrolled`, and a suppressed or unsubscribed address 409 `contact_unreachable`. An address or id nobody has is 404 `contact_not_found`.
- Sending neither or both of `email` and `contactId` is 422 `invalid_parameter`.
- Not retried automatically. After a lost response, a second attempt that answers 409 `already_enrolled` means the first one worked.

Also available in: API [`POST /automations/{id}/enrollments`](https://openemail.uk/docs/api/reference/automations#post-automations-id-enrollments); 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); 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).

### `automations->exitEnrollment`

Take a contact out of an automation

```php
exitEnrollment(string $id, string $enrollmentId, ?string $apiKey = null): array
```

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. It takes no body.

Scopes: `automations:write`.

**Parameters**

- `id` (`string`, required): Automation id such as `aut_5c1e9a7b3d2f48e6a0b4c7d1`.
- `enrollmentId` (`string`, required): Enrollment id such as `aen_2b8d4f6a1c3e5079b6d8f0a2`, from `listEnrollments`.
- `apiKey` (`string`): Overrides the client's API key for this call only.

**Returns**

An array, the enrollment shaped like an item of `listEnrollments`, with `status` `exited`, `exitReason` `removed` and `finishedAt` set.

**Example**

```php
$enrollment = $client->automations->exitEnrollment('aut_5c1e9a7b3d2f48e6a0b4c7d1', 'aen_2b8d4f6a1c3e5079b6d8f0a2');

echo $enrollment['status'], ' (', $enrollment['exitReason'], ') at ', $enrollment['finishedAt'], PHP_EOL;
```

**Notes**

- An enrollment that already completed or left answers 409 `automation_enrollment_finished`.
- Not retried automatically. After a lost response, a second attempt that answers 409 `automation_enrollment_finished` means the first one worked.

Also available in: API [`POST /automations/{id}/enrollments/{enrollmentId}/exit`](https://openemail.uk/docs/api/reference/automations#post-automations-id-enrollments-enrollmentid-exit); 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); 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).
