Automations
Define a sequence once, enrol a person, and let it end itself.
An automation is a multi-step sequence defined here rather than as scheduled sends in the calling app. Enrol someone and each step goes out after its delay. The reason to prefer it over four scheduled emails is the exits: the sequence stops on its own when the person unsubscribes, replies, or bounces — the three things you would otherwise have to track and cancel by hand.
Defining one
POST /v1/automations takes a name, a slug, the from address, its identity_id, a trigger and one to twenty steps. Each step has delay_minutes — measured from enrolment for the first, from the previous step for the rest — a subject, and either inline html or a template_slug rendered with the enrolment's variables. exit_on_reply defaults to true; turn it off for something like a dunning sequence that should continue after a reply.
Triggers: api for enrolling by call, contact_added with an audience_id to start when someone joins a list, or event with an event_name to start on a named event from your app.
Every step sends as marketing — a sequence is bulk mail, and that is what gets it an unsubscribe footer — so from must sit on a verified marketing domain, and identity_id must be that domain's. Both are checked when the automation is created, with 422 no_verified_identity or 422 invalid_request, rather than surfacing later as a failed enrolment per contact.
A new automation is a draft. POST /v1/automations/{id}/status with { "status": "active" } turns it on; paused stops it, and enrolments already in flight are cancelled at their next step rather than left running.
{
"name": "Onboarding",
"slug": "onboarding",
"from": "Ana <ana@news.example.com>",
"identity_id": "…",
"trigger": { "kind": "api" },
"steps": [
{ "delay_minutes": 0, "subject": "Welcome", "template_slug": "welcome" },
{ "delay_minutes": 4320, "subject": "Three things to try", "html": "<p>…</p>" },
{ "delay_minutes": 10080, "subject": "How is it going?", "html": "<p>…</p>" }
]
}Enrolling someone
POST /v1/automations/{id}/enroll with an email and optional variables returns 201 with enrolled: true and the enrolment. Enrolling the same person twice returns 200 with enrolled: false and reason: "already_enrolled" — a retried call looks the same as the first, and the guarantee is a unique index on active enrolments rather than a check, so it holds under concurrency too.
A suppressed address answers 200 with reason: "suppressed" rather than creating a row that can only ever fail. An automation that is not active, or has no steps, answers 409.
curl -X POST https://api.example.com/v1/automations/$AUTOMATION_ID/enroll \
-H "Authorization: Bearer $API_KEY" \
-d '{"email": "ana@example.com", "variables": {"name": "Ana", "plan": "Pro"}}'Events
POST /v1/events with a name, an email and optional variables enrols the person in every active automation whose trigger waits on that event, and returns automations_started. One call from your app — trial_started, invoice_overdue — instead of it knowing which sequences exist.
curl -X POST https://api.example.com/v1/events \
-H "Authorization: Bearer $API_KEY" \
-d '{"name": "trial_started", "email": "ana@example.com"}'Three automatic exits
| Exit | What happens |
|---|---|
| Unsubscribe | Every active enrolment for the address is cancelled with reason unsubscribed, along with any sends already scheduled. Cancelling the sends alone would stop one email and let the sequence generate the next — which reads to the recipient as an unsubscribe that did not work. |
| Reply | An inbound reply cancels the enrolment with reason replied, for every automation with exit_on_reply. A drip that keeps arriving after someone has answered is the most common way a sequence reads as broken. |
| Hard bounce or complaint | The address is suppressed, and the enrolment is cancelled with reason suppressed when its next step comes due. Suppression is checked before every step, not only at enrolment, so someone who opted out on day two does not receive day three. |
Editing a live automation
Steps are read from the automation each time one is due rather than snapshotted at enrolment, so a fix to step three reaches people already partway through. A templated step whose variables are missing marks that enrolment failed with the reason rather than retrying — it is a definition problem, and would fail identically every time.
GET /v1/automations/{id} returns the definition with enrolment counts by status and the most recent enrolments. DELETE /v1/automations/{id} cancels every active enrolment and reports enrollments_canceled.
Next: Topics and preferences