---
title: "Run an automation for a contact"
method: POST
path: "/v1/sites/{site_id}/automations/{automation_id}/runs"
tags: ["Automations"]
---

# Run an automation for a contact

`POST /v1/sites/{site_id}/automations/{automation_id}/runs`

## Request

- Base URL: `https://api.getsitecontrol.com`
- URL: `https://api.getsitecontrol.com/v1/sites/{site_id}/automations/{automation_id}/runs`
- Auth: HTTP bearer

Queues a run of the automation for one contact and answers 202 at once. Runs
an automation that is on and has the `manual` or the `webhook`
trigger (find them with `list_automations` by `trigger_type`, `is_active`
set to `true`). Needs the `emails.full` permission.

The body names the contact by `contact_id` or gives it as `contact`, exactly
one of the two. `contact` is created or updated by e-mail as
`upsert_contact` does, then run; a status the contact cannot take refuses
the run and writes nothing. It needs `contacts.edit` on top of
`emails.full` and spends the contact creation limit, even when the
automation refuses the run. A
limit hit here is not kept under the `Idempotency-Key`: the same key runs
again once the window passes. The contact and the run are saved together:
on 202 both are; on any error, neither is.

The two triggers run differently:

- `manual` is a sequence of steps that other automations start
  too. A contact is in it once at a time: while its run is queued or going
  on, a new run is refused. Once the run finishes, the contact may be run
  again. Other manual starts (the dashboard, contact imports, the "start
  automation" step) honour the same check; starts by other trigger types and
  bulk event packs are not seen by it. A run that failed may be repeated at
  once through this operation only: the other manual starts skip a contact
  whose run in this automation failed. `data` is not read.
- `webhook` takes an event with its values: every call is a new run, also
  for a contact already in the automation. `data` reaches the steps as
  `{{$webhook.<key>}}`, the values its e-mails and conditions use; the
  contact is `{{$contact.<field>}}`. The automation's `webhook_url` from
  `get_automation` starts the same automation without credentials, for a
  service that can only send a request: it takes a flat body, finds the
  contact in it and answers before the automation is checked. Its whole
  body is `$webhook`, while here `$webhook` holds only `data`: a value the
  automation reads from `$webhook`, the e-mail included, goes into `data`.

The response carries no run id: the run waits to start; follow it with
`list_automation_runs` by `contact_id` and `order=newest`. Send an `Idempotency-Key` to
retry safely: a retry with the same key gets the stored 202, not a 409.

An unsubscribed contact is run as well, but the steps send it no e-mails.

## Path parameters

- `site_id` integer, required
- `automation_id` integer, required: Automation id from `list_automations`

## Headers

- `Idempotency-Key` string: Makes a retry of a POST safe: a repeat with the same key, body and GSC-Version returns the stored response with `Idempotency-Replayed: true` instead of running again. The key protects against repeated execution while the key store works. Saving the change and storing the response under the key are not atomic: if the process or the key store fails between them, the change is kept, the key is released after 15 minutes and a retry runs the request again. After a timeout or a 5xx, read the resource to check the result before retrying. 5xx and 429 responses and responses over 256 KB are not stored, so a repeat runs the request again. A key is kept for 24 hours. The same key with another body or version is 422; a repeat while the first request runs is 409.

## Request body

- AutomationRunCreate: Exactly one of `contact_id` and `contact`.
  - `contact_id` integer: Id of an existing contact of the site, from `list_contacts`; leave it out when `contact` is given
  - `contact` ContactUpsert: The fields every contact write takes; an upsert finds by `email`.
    - `email` string, required: Finds the contact, without case; kept as stored when found, stored in lower case when created
    - `status` 'subscribed' | 'unsubscribed': `subscribed` or `unsubscribed`; `bounced` is set only by e-mail delivery. Left out: unchanged, a new contact is subscribed; may not be null
    - `first_name` string, nullable: null clears it
    - `last_name` string, nullable: null clears it
    - `country` string, nullable: ISO 3166-1 alpha-2 code or an English country name; stored as the code; null clears it
    - `phone` string, nullable: In the international format, or national with `country` in the same request; stored normalized; null clears it
    - `timezone` string, nullable: IANA time zone name such as `Europe/Berlin`; null clears it
    - `language` string, nullable: ISO 639-1 code such as `de`; stored in lower case; null clears it
    - `fields` object: Values by field id from `list_contact_fields`: a string for text, a whole number for integer, a number, an ISO 8601 string for a datetime (UTC without an offset), YYYY-MM-DD for a date, true or false for a boolean. Fields of the other type are read-only. Merged into the stored values: null removes a value, keys not given stay as they are
    - `add_tags` string[]: Tag names to add, compared without case; a tag the site does not have is created. Tags the contact has stay
    - `remove_tags` string[]: Tag names to remove, compared without case; a name with no tag, or a tag the contact does not have, is no change. Never creates a tag
  - `data` object, nullable: Values for the steps of an automation with the `webhook` trigger: each key is `{{$webhook.<key>}}` in its e-mails and conditions. `$webhook` holds only these values; the contact is `{{$contact.<field>}}`. A `manual` automation does not read them.

## Response `202`

Successful Response

- AutomationRunQueued
  - `automation_id` integer, required
  - `contact_id` integer, required: The contact run; for `contact`, the one created or found

## Other responses

- `400`: Unsupported GSC-Version
- `401`: Missing or invalid API key or access token
- `403`: `forbidden`: no `emails.full` permission, or no `contacts.edit` for `contact`; `account_suspended`: the site is suspended; `feature_unavailable`: the plan has no automations
- `404`: `site_not_found`, `automation_not_found`; `contact_not_found`: no contact of the site with `contact_id`
- `409`: `automation_inactive`: the automation is off, turn it on with `enable_automation`; `automation_not_startable`: its trigger is neither `manual` nor `webhook`; `run_in_progress`: the contact is in this `manual` automation, `metadata.state` is `queued` (waiting to start) or `ongoing` (going on, waiting in a delay or on hold); `contact_email_taken`: another request took the e-mail of `contact` at the same moment; `idempotency_in_progress`: a request with the same `Idempotency-Key` is still running
- `413`: Request body too large
- `422`: `validation_failed`; for `contact`, a field, tag or status transition refused, or the contact unreadable; `idempotency_key_reused`
- `429`: Rate limited
- `503`: `temporarily_unavailable`: for `manual`, a bulk start of this automation was being saved and the call waited for it longer than the 25-second request timeout, or rate limits or idempotency keys cannot be enforced; retry later

---

[API](https://skmtc.dev/getsitecontrol/apis/getsitecontrol-public-api.md) - [All operations](https://skmtc.dev/getsitecontrol/apis/getsitecontrol-public-api/llms.txt) - [OpenAPI document](https://skmtc.dev/getsitecontrol/apis/getsitecontrol-public-api/revisions/084fcad80e75?raw)
