# Automations

Run steps when something happens in Zernio, check posts or ads on a schedule, and keep every automation in one API.

`/v1/automations` holds three kinds of automation in one shape: a `trigger`, what it checks, the `steps` it takes and its `limits`.

- **Event automations** run when something happens in Zernio, like a post failing or an order coming in.
- **Scheduled automations** check a profile's recent posts on a schedule, and can boost the winners.
- **Ad rules** check campaigns, ad sets or ads on a schedule and change them ([ad rules guide](/guides/ad-rules)).

`GET /v1/automations/catalog` lists every event, fact and step the API accepts.

## Alert when a post fails

An event automation names the event in `trigger.type` and the profile it watches. This one sends a Slack message when a post fails on Instagram:

```bash
curl -X POST https://zernio.com/api/v1/automations \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Failed Instagram posts to Slack",
    "trigger": {
      "type": "post.failed",
      "profileId": "6731a0c2e4b0f1a2b3c4d5e6",
      "filter": { "field": "platform", "op": "equals", "value": "instagram" }
    },
    "steps": [{ "type": "notify.send", "to": [{ "accountId": "6731a0c2e4b0f1a2b3c4d5f7", "channelId": "C0123456789" }] }],
    "limits": { "cooldown": "1h" }
  }'
```

`to` is a connected Slack account and channel, or a connected Gmail or Outlook account and an email address.

An event or scheduled automation is on as soon as it is created, unless you send `"status": "paused"`.

## Boost posts that take off

A scheduled automation checks posts with `subjects.each: "post"`. This one looks at the last 3 days of posts every hour and boosts each one past 1,000 likes, once:

```bash
curl -X POST https://zernio.com/api/v1/automations \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Boost viral posts",
    "trigger": { "type": "schedule", "every": "1h" },
    "subjects": { "each": "post", "profileId": "6731a0c2e4b0f1a2b3c4d5e6", "publishedWithin": "3d" },
    "filter": { "field": "likes", "op": "greater_than", "value": 1000 },
    "steps": [
      { "type": "ads.boost_post", "adAccountId": "act_1234567890", "budget": 50, "days": 5, "goal": "engagement", "countries": ["US", "CA"] },
      { "type": "notify.send", "to": [{ "accountId": "6731a0c2e4b0f1a2b3c4d5f7", "channelId": "C0123456789" }] }
    ],
    "limits": { "maxChangesPerDay": 3 }
  }'
```

`budget` is the whole spend of one boost, in the ad account currency. Only Facebook and Instagram posts are boosted, and boosting needs the Ads add-on.

## Ask for approval before an ad rule acts

Add an `approval.request` step before the change. Each change then waits until one of `to` approves it, for `expiresIn` at most. Approvers are team members who can open the rule's profile:

```json
"steps": [
  { "type": "approval.request", "to": ["ana@example.com"], "expiresIn": "1d" },
  { "type": "ads.pause" }
]
```

`GET /v1/automations/{automationId}/approvals` lists what is waiting. Approve or reject with `POST /v1/automations/{automationId}/approvals/{approvalId}/approve` and `POST /v1/automations/{automationId}/approvals/{approvalId}/reject`.

## Try an ad rule on past data

`POST /v1/automations/backtest` replays a draft ad rule over the last 30 days (up to 90 with `?days=90`) and returns what it would have changed each day.

`POST /v1/automations/{automationId}/backtest` does the same for a saved one. Nothing changes on the ad account.

## Install a template

`GET /v1/automations/templates` lists your team's templates. Zernio's own are read and installed by id under `/v1/automations/templates/zernio`, and `GET /v1/ads/rules/templates` lists their ids.

Install one on several ad accounts in one call, one `targets` entry each:

```bash
curl -X POST https://zernio.com/api/v1/automations/templates/zernio/stop_losers/install \
  -H "Authorization: Bearer $ZERNIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "targets": [
      { "inputs": { "adAccount": { "accountId": "6731a0c2e4b0f1a2b3c4d5e6", "adAccountId": "act_1234567890" } } },
      { "inputs": { "adAccount": { "accountId": "6731a0c2e4b0f1a2b3c4d5e6", "adAccountId": "act_9876543210" } } }
    ]
  }'
```

Each target installs on its own, all or nothing. The answer is `201` when every target installed and `207` when some failed.

When none did, it is the first target's error, with each result in `details.installations`. New automations start paused unless you send `"status": "active"`.

## Stop every ad automation

`PATCH /v1/automations/controls` with `{ "paused": true }` stops every ad rule of the team at once, and `{ "paused": false }` lets them run again. Each rule keeps its own status. Automations that boost posts are not stopped by it.

`PATCH /v1/automations/controls/ad-accounts` does the same for one ad account, and its `dailyRaiseLimit` caps how much ad rules may raise budgets there in any 24 hours.

## How it behaves

- **Checks**: `GET /v1/automations/{automationId}/checks` lists every check, what matched and what each step did.
- **Changes**: `GET /v1/automations/actions` is the feed of every change ad rules made.
- **Webhooks**: [automation webhooks](/webhooks/automations) announce each check, change and alert.
- **Errors**: a refused automation answers `400` with each problem and its JSON path in `details.problems`.
- **API keys**: a key limited to some resource groups needs the `ads` group for every kind of automation, alerts on posts included.
- **Edits**: `PATCH /v1/automations/{automationId}` takes the fields you change, with the `version` you read as `If-Match`.

## Related

- [Create automation](/automations/create-automation): every field of each kind.
- [Get automation catalog](/automations/get-automation-catalog): events, facts and steps.
- [Automated ad rules](/guides/ad-rules): conditions, windows and ad steps.
- [Automation webhooks](/webhooks/automations): check, action and alert events.