Automations
Build workflows from triggers and steps, run them, and inspect their runs.
- POST/automations
- GET/automations/{id}
- POST/automations/{id}
- GET/automations
- DEL/automations/{id}
- POST/automations/{id}/start
- POST/automations/{id}/pause
- POST/automations/{id}/stop
- POST/automations/{id}/trigger
- GET/automations/{id}/runs
- GET/automations/{id}/runs/{run_id}
- POST/automations/{id}/runs/{run_id}/cancel
- POST/automations/{id}/exit
- GET/automations/runs
- GET/automations/{id}/stats
- GET/automations/{id}/steps/{step_key}/stats
Create an automation
Creates an automation in draft status from a graph of steps and connections. Requires an API key with the full scope. Automations are in beta.
The automation does nothing until you start it. Each run costs 3 credits when it starts, and every email sent by send_email or forward_email costs 1 more credit. In an unverified workspace, those actions can only send to workspace members’ account emails.
/automationsBody parameters
contextstringrequiredcontact, email or event. The context decides which triggers and actions you can use and can’t be changed later. See Contexts.namestringrequireddescriptionstring | nullsettingsobjectstepsobject[]requiredconnectionsobject[]required[] for a graph with only a trigger. See Connections.Settings
exit_conditionsobject[]Cancel a contact’s run as soon as one of these matches (contact context, up to 20). Each item is an event matcher, the trigger name in event plus its config fields, such as { "event": "contact.tag_added", "tag": "customer" } or { "event": "contact.on_event", "event_name": "purchase" }, or a condition on the contact, { "condition": { "match": "all", "rules": [...] } }, with the rules of the condition action. Matchers take every contact trigger except the date triggers and contact.visits_url, and an optional filter like trigger configs. Segment events (segment_id required) and commerce events are alpha, like their triggers. contact.on_event matchers, here and in goal and wait_until steps, need a Pro, Business or Custom plan. See Exit conditions and goals.
exit_on_audience_removalbooleandefault: falsetrue cancels a run started by contact.added_to_audience when the contact is removed from that audience, unsubscribes from it, or unsubscribes from all marketing email.on_step_failurestringdefault: stopstop marks the run failed when a step fails. skip records the failed step and continues on its default edges (or no after a failed condition).allow_reentrybooleandefault: truefalse skips a trigger when the same contact (or email) already has a running run in this automation.max_concurrent_runsintegerdefault: 0running status at once. 0 means no limit.cooldown_secondsintegerdefault: 0Steps
keystringrequiredwelcome_email. Connections, step statistics and updates refer to steps by key.typestringrequiredtrigger or action.triggerstringactionstringconfigobjectConnections
fromstringrequiredtostringrequiredbranchstringdefault: defaultfrom step follows this edge. condition steps use yes and no; wait_until steps use matched and timeout (events and conditions) or default and passed (dates); experiment steps use variant keys. Every other step uses default.Contexts
| Context | A run is about | Triggers | Rules |
|---|---|---|---|
contact |
One contact. send_email goes to that contact. |
contact.*, system.* |
One or more triggers. They must all connect to the same first action. |
email |
One email (sent or received). | email.*, system.* |
One or more triggers. They must all connect to the same first action. |
event |
The trigger payload only. | event.*, system.* |
Exactly one trigger. |
Every action must be reachable from a trigger. A run starts at the action connected to the trigger that fired and follows connections:
conditionfollows only the edge whosebranchisyesorno, depending on the result.wait_untilfollows onlymatchedortimeout(events and conditions), ordefaultorpassed(dates).experimentfollows the edges of the chosen variant. When that path ends, the run continues on the experiment step’sdefaultedges.waitdelays the next step.- Every other action follows its
defaultedges. A step with several outgoing edges runs all of them. - A
goalstep is also reached out of order: when the contact meets the goal, the run jumps to it and continues after it.
A run is completed when no steps are left or an end_run step runs, failed when a step fails (with on_step_failure: "stop"), and canceled when an exit condition matches, the run is canceled, or you stop the automation.
Triggers
| Trigger | Context | Fires when |
|---|---|---|
contact.added_to_audience |
contact | A contact subscribes to an audience, including resubscribes. |
contact.removed_from_audience |
contact | A subscriber is deleted from an audience. |
contact.added_to_segment |
contact | A contact joins a tracked segment. Not for changes caused by editing the segment. Alpha: only the Emailit team can add segment triggers; other requests return 403 with error: "segments_alpha". |
contact.removed_from_segment |
contact | A contact leaves a tracked segment. Not for changes caused by editing the segment. Alpha, like contact.added_to_segment. |
contact.created |
contact | A contact is created. |
contact.updated |
contact | A contact is updated, including tags and score. |
contact.tag_added, contact.tag_removed |
contact | A tag is added to or removed from the contact. |
contact.score_changed |
contact | The contact’s lead score changes. |
contact.unsubscribed |
contact | The contact unsubscribes from an audience or from all marketing email. |
contact.loaded_email |
contact | A recipient who is a contact in the workspace opens an email. |
contact.clicked_in_email |
contact | A recipient who is a contact in the workspace clicks a tracked link. |
contact.on_date |
contact | A date custom field comes, optionally offset by days or weeks, at a time of day in a timezone. Once, or yearly with repeat: "yearly". |
contact.date_anniversary |
contact | Every year on the month and day of a date custom field, with the same options. |
contact.on_event |
contact | A custom event (c:) or workspace event (w:) for the contact. Business and Custom plans: other plans get 403 with error: "plan_required" and required_plan: "business". |
form.submitted |
contact | A form is submitted with an email address. |
contact.on_purchase |
contact | A contact places a new order. Alpha: only the Emailit team can add commerce triggers; other requests return 403 with error: "commerce_alpha". |
contact.abandoned_cart |
contact | A contact’s cart is abandoned. Alpha, like contact.on_purchase. |
contact.visits_url |
contact | Accepted, but Emailit doesn’t fire it yet. |
email.received |
An inbound email arrives. | |
email.delivered, email.bounced, email.complained, email.loaded, email.clicked, email.failed, email.suppressed, email.canceled |
The email event of the same name occurs. | |
event.<name> |
event | Any name that starts with event.. Emailit doesn’t emit event.* events yet; start event automations with system.manual. |
system.manual |
all | You call Trigger a run. |
system.schedule |
all | Accepted, but Emailit doesn’t fire scheduled triggers yet. |
Trigger config fields:
audience_idstringcontact.added_to_audience and contact.removed_from_audience: only fire for this audience (aud_…).segment_idstringcontact.added_to_segment and contact.removed_from_segment: the tracked segment (seg_…). Optional when you create the automation, required when you update its steps. A segment that doesn’t exist or isn’t tracked returns 400.include_current_membersbooleandefault: falsecontact.added_to_segment: on the automation’s first start, also start a run for every contact already in the segment. Re-entry settings still apply.date_fieldstringcontact.date_anniversary and contact.on_date: the custom field key that holds the date, for example birthday.offset, time, timezone, repeatobject, string, string, stringoffset is { "amount": 3, "unit": "days", "direction": "before" } (unit days or weeks), time is HH:MM (default 00:00), timezone an IANA name (default UTC), and repeat once or yearly for contact.on_date.tagstringcontact.tag_added and contact.tag_removed: only this tag.threshold, directionnumber, stringcontact.score_changed: only when the score crosses threshold, going up (default) or down.fieldstringcontact.updated: only when this field changed, for example custom_fields.purchased, tags or score.event_namestringcontact.on_event: the event name, such as purchase (matches c:purchase and w:purchase) or w:webinar.attended. Leave it out for any workspace or custom event. Filter on the event’s properties with filter, for example properties.total.automation_id, campaign_id, link_containsstringform_idstringform.submitted: only this form.filterobjectOnly fire when the event matches: { "match": "all", "rules": [{ "field": "subject", "operator": "contains", "value": "Invoice" }] }. match is all (default) or any. field is a dotted path into the event’s object, for example to or email.subject; a leading payload. is ignored.
Operators: equals, not_equals, contains, not_contains, starts_with, ends_with, greater_than, less_than, in, not_in, is_set, is_not_set. Every operator except is_set and is_not_set needs a value; in and not_in take an array.
Actions
| Action | Context | Config |
|---|---|---|
send_email |
all | type (required, template), template_id (required: a tem_ ID or the alias of a published template), from, subject, reply_to, to, email_type, tracking |
forward_email |
email, event | to (required), from, subject, email_id |
wait |
all | seconds (required, 0 to 2,592,000, which is 30 days) |
wait_until |
all (event and condition modes: contact) |
mode (date, event or condition) and the fields of that mode |
condition |
all | filter (required, see Conditions) |
goal |
contact | event or condition, if_not_met |
experiment |
all | variants (required), control |
call_webhook |
all | url (required), method, headers, body, secret, fail_on_error |
run_automation |
all | automation_id (required), exit_current |
exit_automation |
contact | automation_ids or all: true |
end_run |
all | None |
end |
all | None |
add_tag, remove_tag |
contact | tags (required) |
update_score |
contact | operation (add, subtract or set), value (required) |
add_to_audience |
contact | audience_id (required), emit_events |
remove_from_audience |
contact | audience_id (required), mode (remove or unsubscribe), emit_events |
update_subscription |
contact | action (subscribe or unsubscribe, required), scope (audience, all_audiences or global), audience_id |
edit_contact |
contact | fields (required), emit_events |
add_to_suppressions |
email, event | type, reason, email |
remove_from_suppressions |
email, event | email |
create_contact |
email, event | email, first_name, audience_id, emit_events |
send_emailsends the template.fromdefaults to the template’s sender and must be on a verified sending domain.subjectoverrides the template subject.reply_tois an address or an array of addresses, and can be a placeholder. In thecontactcontext the email goes to the run’s contact; in theemailandeventcontexts setto.email_typeismarketing(default in thecontactcontext: skipped for unsubscribed contacts, with an unsubscribe link andList-Unsubscribeheaders) ortransactional.tracking: { "loads": true, "clicks": true }overrides the domain’s tracking settings, andtracking: {}goes back to them.wait_untilwaits for a date:{ "mode": "date", "date": { "type": "global", "key": "webinar_date" }, "offset": { "amount": 1, "unit": "days", "direction": "before" }, "time": "09:00", "timezone": "Europe/Prague", "if_passed": "continue" }, wheredate.typeiscontact(withfield),global(withkey),fixed(withvalue) orpayload(withpath), andif_passediscontinueorbranch. For an event:{ "mode": "event", "event": { "event": "contact.clicked_in_email" }, "timeout": { "amount": 2, "unit": "days" } }. For a condition:{ "mode": "condition", "condition": { ... }, "timeout": ... }. Waits last up to 365 days. See Wait until.goaltakes an event matcher inevent(as inexit_conditions) or acondition. When the contact meets it, the run jumps to the goal.if_not_met(continue,waitorend) decides what happens when a run reaches the goal before meeting it. Goal steps need a Pro, Business or Custom plan.forward_emailforwards the run’s email (or the email inemail_id) toto.fromdefaults to the original sender andsubjecttoFwd: <original subject>.conditiontakes{ "match": "all" | "any", "rules": [...] }. See Conditions. The step continues onyesorno.experimentpicks one ofvariants(andcontrol), each{ "key": "a", "weight": 50 }, at random by weight, and continues on the branch named after the chosen key.call_webhooksends an HTTP request (default methodPOST, JSONContent-Type) and records the status class (2xx,4xx,5xx) ortimeoutornetwork_error. Withoutbody, it sends the automation, run, contact and payload as JSON. Withsecret, the body is signed inX-Emailit-Signature. A non-2xx response doesn’t fail the step unlessfail_on_erroristrue. Private addresses are blocked.run_automationstarts a run of another running automation for the same contact, with this run’s payload. The current run continues, or ends withexit_current: true.exit_automationcancels the contact’s running runs in the automations inautomation_ids, or in every other automation withall: true.end_runends the run, including other branches.endonly ends its own branch.add_tagandremove_tagchange the contact’s tags.update_scorechanges the lead score.update_subscriptionsubscribes or unsubscribes the contact from one audience, every audience (unsubscribeonly), or all marketing email (global).remove_from_audiencedeletes the contact’s membership (mode: "remove", the default) or keeps them on the audience as unsubscribed (mode: "unsubscribe").emit_events(defaulttrue) onadd_to_audience,remove_from_audience,create_contactandedit_contactsends the usualsubscriber.*andcontact.*events, so webhooks get them and other automations start. Steps saved before 2026-10-20 haveemit_events: false, and theirremove_from_audiencesteps havemode: "unsubscribe".edit_contacttakesfields: [{ "key": "first_name", "value": "Ada" }]. The keysemail,first_name,last_nameandunsubscribedupdate the contact; any other key (with or withoutcustom_fields.) sets a custom field, converted to its type.operationisset(default),clear,incrementordecrement.add_to_suppressionssuppresses the run’s address (defaulttyperecipient, defaultreasonautomation).remove_from_suppressionsremoves it. In theeventcontext, passemail.create_contactcreates the contact (or finds the existing one) and optionally subscribes it toaudience_id. In theemailcontext,emaildefaults to the email’s recipient.
String values in any action config can use placeholders that Emailit fills in when the step runs: {{contact.email}}, {{cf.plan}}, {{global.webinar_link}}, {{email.mail_from}}, {{payload.object.subject}} or {{meta.source_event_id}}, for example "to": "{{email.mail_from}}". Templates sent by send_email, and its subject, are rendered with Temple and also have the contact’s fields directly, such as {{ first_name }}, {{tags}} and {{score}}.
Conditions
A condition filter (also wait_until and goal conditions and exit_conditions) is { "match": "all" | "any", "rules": [...] }. A rule is { "field", "operator", "value" }, or a nested { "match", "rules" } group, up to 3 levels and 50 rules.
field |
operator |
value |
|---|---|---|
Contact fields (email, first_name, last_name, created_at, unsubscribed, score), subscribed, custom_fields.<key>, global.<key>, payload.<path>, meta.<path>, contact.<path>, email.<path> |
equals, not_equals, contains, not_contains, starts_with, ends_with, in, not_in, is_set, is_not_set, greater_than, less_than, greater_or_equal, less_or_equal, is_true, is_false, before, after, on, is_past, is_future, within_last, within_next, more_than_ago, more_than_from_now |
The value. Day operators take days or { "amount", "unit" }. Not needed for is_set, is_not_set, is_true, is_false, is_past, is_future. |
tags |
contains, not_contains, in, not_in, is_set, is_not_set |
A tag, or an array for in and not_in. |
audience |
is_subscribed, is_not_subscribed, is_member, is_not_member |
An audience ID (aud_…). |
engagement |
opened, not_opened, clicked, not_clicked |
"run", "automation", { "scope": "step", "step": "<key>" }, { "scope": "any", "days": 30 } or { "scope": "campaign", "campaign_id": "cmp_…" }. |
segment |
is_member, is_not_member |
A segment ID (seg_…), tracked or not. Alpha. |
commerce.<field> |
The field’s segment operators, except five that use this table’s names: greater_or_equal, less_or_equal, within_last, within_next and more_than_ago |
The value the segment rule takes. Alpha. See below. |
Bare fields resolve against the run’s contact (first_name, custom_fields.plan) or email (rcpt_to, subject). subscribed is true when the contact can get marketing email.
commerce.<field> rules use the commerce fields of segments, evaluated the same way: orders_count, total_spent, average_order_value, first_order_at, last_order_at, expected_next_order_at, last_active_at, churn_risk, purchased, viewed_product, used_coupon and cart, for example { "field": "commerce.purchased", "operator": "has", "value": { "days": 30, "product_ids": ["sku-1"] } }. Segment and commerce rules are in alpha: other requests that add them return 403 with error segments_alpha or commerce_alpha. Automations that already have them keep working for everyone.
Returns
Returns 201 Created with the automation in data, including each step’s ID (aus_…) and the connections. status is draft.
Create checks the graph structure: trigger and action names for the context, unique keys, valid connections and branches, reachability, the settings, and that any send_email from address uses a verified sending domain. It doesn’t check that every action config is complete; Update an automation does. Errors return 400 with errors keyed by field path.
Features the workspace’s plan doesn’t include return 403 with error: "plan_required", the lowest plan that has them in required_plan, and a message. goal steps and contact.on_event matchers in exit_conditions, goal and wait_until need pro, and the contact.on_event trigger needs business.
{
"data": {
"id": "aut_3xqC9YD79FZZA36uTekWTBO1ghe",
"context": "contact",
"name": "Welcome series",
"description": "Welcome new subscribers, then nudge free users a day later.",
"status": "draft",
"settings": { "allow_reentry": false },
"last_triggered_at": null,
"published_at": null,
"created_at": "2026-10-01T09:41:05.318274+00:00",
"updated_at": "2026-10-01T09:41:05.318274+00:00",
"steps": [
{
"id": "aus_3ZqpuxjmL1szZ8og6uGL1mT6QEJ",
"key": "joined",
"type": "trigger",
"trigger": "contact.added_to_audience",
"action": null,
"config": { "audience_id": "aud_3Cz7WJ9GpvD7P0PQez50xOmkZSV" }
},
{
"id": "aus_3V5TZIXXak1SSmGcOmzPOeFaguQ",
"key": "welcome_email",
"type": "action",
"trigger": null,
"action": "send_email",
"config": { "type": "template", "template_id": "welcome", "from": "Acme <hello@acme.com>" }
},
{
"id": "aus_3vM0ORwEXCQcyS7rylfQBQEXjVk",
"key": "wait_1_day",
"type": "action",
"trigger": null,
"action": "wait",
"config": { "seconds": 86400 }
},
{
"id": "aus_3HOgOzxaXBgVRpLFtpvJNo4vd5c",
"key": "is_free_user",
"type": "action",
"trigger": null,
"action": "condition",
"config": {
"filter": {
"match": "all",
"rules": [{ "field": "custom_fields.plan", "operator": "is_not_set" }]
}
}
},
{
"id": "aus_3gCI5SWMPFVhOSawR6nz8sF55wp",
"key": "upgrade_tips",
"type": "action",
"trigger": null,
"action": "send_email",
"config": { "type": "template", "template_id": "getting-started-tips" }
},
{
"id": "aus_3Pq7Wd2LxN8cVt5RmK0sHy4BfJe",
"key": "done",
"type": "action",
"trigger": null,
"action": "end",
"config": {}
}
],
"connections": [
{ "from": "joined", "to": "welcome_email", "branch": "default" },
{ "from": "welcome_email", "to": "wait_1_day", "branch": "default" },
{ "from": "wait_1_day", "to": "is_free_user", "branch": "default" },
{ "from": "is_free_user", "to": "upgrade_tips", "branch": "yes" },
{ "from": "is_free_user", "to": "done", "branch": "no" }
]
},
"message": "Automation was successfully created.",
"notify": true
}{
"message": "Validation failed.",
"errors": {
"steps.1.action": ["Action \"forward_email\" is not allowed for context \"contact\"."],
"steps": ["Action step \"upgrade_tips\" is not reachable from any trigger."]
}
}Retrieve an automation
Retrieves an automation with its full graph. Requires an API key with the full scope.
/automations/{id}Path parameters
idstringrequiredaut_…).Returns
Returns the automation in data.
idstringaut_.contextstringcontact, email or event.namestringdescriptionstring | nullstatusstringdraft, running, paused, stopped or archived.settingsobjecton_step_failure, allow_reentry, max_concurrent_runs, cooldown_seconds. Empty when you haven’t set any.last_triggered_atstring | nullpublished_atstring | nullstepsobject[]id (aus_…), key, type, trigger, action and config. Run details refer to steps by id; statistics refer to them by key.connectionsobject[]from and to step keys and its branch.See Create an automation for the meaning of every trigger, action and setting. Returns 404 if the automation doesn’t exist or was deleted.
{
"data": {
"id": "aut_3xqC9YD79FZZA36uTekWTBO1ghe",
"context": "contact",
"name": "Welcome series",
"description": "Welcome new subscribers, then nudge free users a day later.",
"status": "running",
"settings": { "allow_reentry": false },
"last_triggered_at": "2026-10-03T14:12:40.551870+00:00",
"published_at": "2026-10-01T10:00:02.204118+00:00",
"created_at": "2026-10-01T09:41:05.318274+00:00",
"updated_at": "2026-10-03T14:12:40.551870+00:00",
"steps": [
{
"id": "aus_3ZqpuxjmL1szZ8og6uGL1mT6QEJ",
"key": "joined",
"type": "trigger",
"trigger": "contact.added_to_audience",
"action": null,
"config": { "audience_id": "aud_3Cz7WJ9GpvD7P0PQez50xOmkZSV" }
},
{
"id": "aus_3V5TZIXXak1SSmGcOmzPOeFaguQ",
"key": "welcome_email",
"type": "action",
"trigger": null,
"action": "send_email",
"config": { "type": "template", "template_id": "welcome", "from": "Acme <hello@acme.com>" }
},
{
"id": "aus_3vM0ORwEXCQcyS7rylfQBQEXjVk",
"key": "wait_1_day",
"type": "action",
"trigger": null,
"action": "wait",
"config": { "seconds": 86400 }
}
],
"connections": [
{ "from": "joined", "to": "welcome_email", "branch": "default" },
{ "from": "welcome_email", "to": "wait_1_day", "branch": "default" }
]
}
}{
"message": "Automation not found."
}Update an automation
Updates an automation’s name, description, settings or graph. Requires an API key with the full scope. The context can’t be changed.
To change the graph, send steps and connections together; they replace the current graph. Steps whose key already exists keep their ID and run history, steps you leave out are deleted, and new keys are added. Unlike create, update also validates every action’s config (for example, send_email needs type and template_id, wait needs seconds). When a step keeps its key and action, settings you leave out of its config keep their saved values: email_type and tracking for send_email, emit_events for contact steps, and mode for remove_from_audience. Send a setting to change it; tracking: {} goes back to the domain’s tracking settings.
Pause the automation before you change the graph of a running automation, then start it again so the new triggers take effect.
/automations/{id}Path parameters
idstringrequiredaut_…).Body parameters
namestringdescriptionstringsettingsobjectexit_conditions, exit_on_audience_removal, on_step_failure, allow_reentry, max_concurrent_runs, cooldown_seconds. See Settings. New exit conditions apply to runs in progress, also on running automations.stepsobject[]connections. See Steps.connectionsobject[]steps. See Connections.Returns
Returns the updated automation in data, with message and notify. Returns 400 with errors keyed by field path when validation fails, 403 with error: "plan_required" when the update adds a step, trigger or exit condition the plan doesn’t include (see Create an automation), and 404 if the automation doesn’t exist. What the automation already has stays allowed after a downgrade.
{
"data": {
"id": "aut_3xqC9YD79FZZA36uTekWTBO1ghe",
"context": "contact",
"name": "Welcome series (v2)",
"description": "Welcome new subscribers, then nudge free users a day later.",
"status": "paused",
"settings": { "allow_reentry": false, "on_step_failure": "skip" },
"last_triggered_at": "2026-10-03T14:12:40.551870+00:00",
"published_at": "2026-10-01T10:00:02.204118+00:00",
"created_at": "2026-10-01T09:41:05.318274+00:00",
"updated_at": "2026-10-04T08:15:22.730115+00:00",
"steps": [
{
"id": "aus_3ZqpuxjmL1szZ8og6uGL1mT6QEJ",
"key": "joined",
"type": "trigger",
"trigger": "contact.added_to_audience",
"action": null,
"config": { "audience_id": "aud_3Cz7WJ9GpvD7P0PQez50xOmkZSV" }
},
{
"id": "aus_3V5TZIXXak1SSmGcOmzPOeFaguQ",
"key": "welcome_email",
"type": "action",
"trigger": null,
"action": "send_email",
"config": { "type": "template", "template_id": "welcome", "from": "Acme <hello@acme.com>" }
}
],
"connections": [
{ "from": "joined", "to": "welcome_email", "branch": "default" }
]
},
"message": "Automation was successfully updated.",
"notify": true
}{
"message": "Validation failed.",
"errors": {
"steps.2.config.seconds": ["Wait seconds cannot exceed 2592000 (30 days)."],
"steps.1.config.template_id": ["Template ID is required when type is \"template\"."]
}
}{
"message": "Automation not found."
}List automations
Returns the workspace’s automations, newest first, without their steps and connections. Requires an API key with the full scope. Deleted automations aren’t listed.
/automationsQuery parameters
pageintegerdefault: 1per_pageintegerdefault: 25filter[context]stringcontact, email or event.filter[status]stringdraft, running, paused, stopped or archived.filter[name]stringsortstringdefault: created_atname, created_at, updated_at or last_triggered_at.orderstringdefault: descasc or desc.You can also use the generic key.condition=value filters on name, status, context and created_at, with match. See Filtering.
Returns
dataobject[]id, context, name, description, status, settings, last_triggered_at, published_at, created_at, updated_at. settings is always an empty object in this list; retrieve the automation to read it.total_recordsintegerper_pageintegercurrent_pageintegertotal_pagesinteger{
"data": [
{
"id": "aut_3xqC9YD79FZZA36uTekWTBO1ghe",
"context": "contact",
"name": "Welcome series",
"description": "Welcome new subscribers, then nudge free users a day later.",
"status": "running",
"settings": {},
"last_triggered_at": "2026-10-03T14:12:40.551870+00:00",
"published_at": "2026-10-01T10:00:02.204118+00:00",
"created_at": "2026-10-01T09:41:05.318274+00:00",
"updated_at": "2026-10-03T14:12:40.551870+00:00"
}
],
"total_records": 1,
"per_page": 50,
"current_page": 1,
"total_pages": 1
}Delete an automation
Deletes an automation. It disappears from lists and stops reacting to triggers. Requires an API key with the full scope.
Runs that are already in progress aren’t canceled. To cancel them, stop the automation before you delete it.
/automations/{id}Path parameters
idstringrequiredaut_…).Returns
Returns a message confirming the deletion. Returns 404 if the automation doesn’t exist or was already deleted.
{
"message": "Automation was deleted successfully.",
"notify": true
}{
"message": "Automation not found."
}Start an automation
Sets the automation’s status to running. From then on, matching triggers start runs; events that happened before you started it don’t. Requires an API key with the full scope.
You can start a draft, paused or stopped automation. The first start sets published_at. On the first start only, contact.added_to_segment triggers with include_current_members: true also start a run for every contact already in their tracked segment.
Starting doesn’t validate the graph again. If you built the automation with Create an automation, which only checks structure, make sure every action config is complete, or send the graph once through Update an automation, which validates it fully. A step with an incomplete config fails when a run reaches it.
/automations/{id}/startPath parameters
idstringrequiredaut_…).Returns
Returns the automation in data with status set to running. Returns 404 if the automation doesn’t exist.
{
"data": {
"id": "aut_3xqC9YD79FZZA36uTekWTBO1ghe",
"context": "contact",
"name": "Welcome series",
"description": "Welcome new subscribers, then nudge free users a day later.",
"status": "running",
"settings": { "allow_reentry": false },
"last_triggered_at": null,
"published_at": "2026-10-01T10:00:02.204118+00:00",
"created_at": "2026-10-01T09:41:05.318274+00:00",
"updated_at": "2026-10-01T10:00:02.204118+00:00",
"steps": [
{
"id": "aus_3ZqpuxjmL1szZ8og6uGL1mT6QEJ",
"key": "joined",
"type": "trigger",
"trigger": "contact.added_to_audience",
"action": null,
"config": { "audience_id": "aud_3Cz7WJ9GpvD7P0PQez50xOmkZSV" }
},
{
"id": "aus_3V5TZIXXak1SSmGcOmzPOeFaguQ",
"key": "welcome_email",
"type": "action",
"trigger": null,
"action": "send_email",
"config": { "type": "template", "template_id": "welcome", "from": "Acme <hello@acme.com>" }
}
],
"connections": [
{ "from": "joined", "to": "welcome_email", "branch": "default" }
]
},
"message": "Automation was successfully started.",
"notify": true
}{
"message": "Automation not found."
}Pause an automation
Sets the automation’s status to paused. Its triggers stop starting new runs, but runs already in progress continue, including those waiting on a wait step. Requires an API key with the full scope.
To also cancel runs in progress, stop the automation instead. Start it again to resume triggering.
/automations/{id}/pausePath parameters
idstringrequiredaut_…).Returns
Returns the automation in data with status set to paused. Returns 404 if the automation doesn’t exist.
{
"data": {
"id": "aut_3xqC9YD79FZZA36uTekWTBO1ghe",
"context": "contact",
"name": "Welcome series",
"description": "Welcome new subscribers, then nudge free users a day later.",
"status": "paused",
"settings": { "allow_reentry": false },
"last_triggered_at": "2026-10-03T14:12:40.551870+00:00",
"published_at": "2026-10-01T10:00:02.204118+00:00",
"created_at": "2026-10-01T09:41:05.318274+00:00",
"updated_at": "2026-10-04T08:10:51.004732+00:00",
"steps": [
{
"id": "aus_3ZqpuxjmL1szZ8og6uGL1mT6QEJ",
"key": "joined",
"type": "trigger",
"trigger": "contact.added_to_audience",
"action": null,
"config": { "audience_id": "aud_3Cz7WJ9GpvD7P0PQez50xOmkZSV" }
},
{
"id": "aus_3V5TZIXXak1SSmGcOmzPOeFaguQ",
"key": "welcome_email",
"type": "action",
"trigger": null,
"action": "send_email",
"config": { "type": "template", "template_id": "welcome", "from": "Acme <hello@acme.com>" }
}
],
"connections": [
{ "from": "joined", "to": "welcome_email", "branch": "default" }
]
},
"message": "Automation was successfully paused.",
"notify": true
}{
"message": "Automation not found."
}Stop an automation
Sets the automation’s status to stopped and cancels every run that is still running; those runs get the status canceled and their remaining steps don’t execute. Requires an API key with the full scope.
Stopping is only available through the API. To keep runs in progress going, pause the automation instead. You can start a stopped automation again; new runs start from scratch.
/automations/{id}/stopPath parameters
idstringrequiredaut_…).Returns
Returns the automation in data with status set to stopped. Returns 404 if the automation doesn’t exist.
{
"data": {
"id": "aut_3xqC9YD79FZZA36uTekWTBO1ghe",
"context": "contact",
"name": "Welcome series",
"description": "Welcome new subscribers, then nudge free users a day later.",
"status": "stopped",
"settings": { "allow_reentry": false },
"last_triggered_at": "2026-10-03T14:12:40.551870+00:00",
"published_at": "2026-10-01T10:00:02.204118+00:00",
"created_at": "2026-10-01T09:41:05.318274+00:00",
"updated_at": "2026-10-05T16:45:09.882301+00:00",
"steps": [
{
"id": "aus_3ZqpuxjmL1szZ8og6uGL1mT6QEJ",
"key": "joined",
"type": "trigger",
"trigger": "contact.added_to_audience",
"action": null,
"config": { "audience_id": "aud_3Cz7WJ9GpvD7P0PQez50xOmkZSV" }
},
{
"id": "aus_3V5TZIXXak1SSmGcOmzPOeFaguQ",
"key": "welcome_email",
"type": "action",
"trigger": null,
"action": "send_email",
"config": { "type": "template", "template_id": "welcome", "from": "Acme <hello@acme.com>" }
}
],
"connections": [
{ "from": "joined", "to": "welcome_email", "branch": "default" }
]
},
"message": "Automation was successfully stopped.",
"notify": true
}{
"message": "Automation not found."
}Trigger a run
Fires the system.manual trigger with a payload you choose. The automation must be running and have a system.manual trigger step; otherwise no run starts. Requires an API key with the full scope. Manual triggers are only available through the API.
The run starts asynchronously and costs 3 credits like any other run. Find it with List runs.
/automations/{id}/triggerPath parameters
idstringrequiredaut_…).Body parameters
payloadobjectData for the run. Emailit adds automation_id and stores the result as the run’s payload. Steps can read it with placeholders such as {{payload.order_id}} and conditions such as payload.plan.
What the run is about depends on the automation’s context:
contact: passcontact_id(con_…). Contact actions andsend_emailuse that contact.email: passemail_id(em_…). Email actions use that email.event: any data. Settooremailin the action configs, for example"to": "{{payload.customer_email}}".
Returns
Returns 200 with a message once the trigger is queued. Returns 422 if the automation isn’t running and 404 if it doesn’t exist.
{
"message": "Automation trigger dispatched."
}{
"message": "Automation must be running to trigger."
}{
"message": "Automation not found."
}List runs
Returns an automation’s runs, newest first. Each run is one pass through the graph, started by a trigger. Requires an API key with the full scope.
/automations/{id}/runsPath parameters
idstringrequiredaut_…).Query parameters
pageintegerdefault: 1per_pageintegerdefault: 25filter[status]stringrunning, completed, failed or canceled.filter[contact_id]stringcon_…). To see a contact’s runs in every automation, use List a contact’s runs.You can also filter with key.condition=value on status, event and created_at (for example created_at.after=2026-10-01), and sort with order and direction on the same keys. See Filtering.
Returns
dataobject[]total_records, per_page, current_page, total_pagesintegerEach run has:
idstringaur_.automation_idstringaut_…).contact_idstring | nullcon_…) in the contact context.email_idstring | nullem_…) in the email context.event_idstring | nulleventstringcontact.added_to_audience or system.manual.payloadobjectmetaobjectfailure_reason.statusstringrunning, completed, failed or canceled.cancel_reasonstring | nullexit_condition, left_audience, canceled_by_user (or the reason you sent), exited_by_api, exited_by_automation, automation_stopped or contact_deleted.started_at, completed_at, created_at, updated_atstring | null{
"data": [
{
"id": "aur_3q6GdFk2eRSG093grI0v9e6REu8",
"automation_id": "aut_3xqC9YD79FZZA36uTekWTBO1ghe",
"contact_id": "con_3munwNLaXKUARc6ff9wPtxKVq4A",
"email_id": null,
"event_id": null,
"event": "contact.added_to_audience",
"payload": {},
"meta": {},
"status": "failed",
"started_at": "2026-10-03T14:12:40.551870+00:00",
"completed_at": "2026-10-03T14:12:41.093355+00:00",
"created_at": "2026-10-03T14:12:40.551870+00:00",
"updated_at": "2026-10-03T14:12:41.093355+00:00"
}
],
"total_records": 1,
"per_page": 25,
"current_page": 1,
"total_pages": 1
}{
"message": "Automation not found."
}Retrieve a run
Retrieves one run of an automation, including the steps it executed. Requires an API key with the full scope.
/automations/{id}/runs/{run_id}Path parameters
idstringrequiredaut_…).run_idstringrequiredaur_…).Returns
Returns the run in data with the fields described in List runs, plus the full payload and meta, a run_steps array and a waits array.
payloadobject | null{ "object": { … } }) or the payload you passed to Trigger a run, with automation_id added.metaobject | nullsource_event_id links the run to the event that started it. Failed runs can have failure_reason: insufficient_credits (the 3 run credits couldn’t be charged) or run_timeout (the run was still running after 72 hours without a step waiting). Canceled runs have cancel_reason, and completed runs that ended early have ended_by, such as end_run_step, moved_to_automation or goal_not_met. Runs started by another automation have depth and either parent_run_id (started by a Start automation step) or caused_by (started by a change a step made).run_stepsobject[]One entry per step the run reached:
step_id: the step’s ID (aus_…), andstep_keyandaction, the step’s key and action.status:running,waiting(awaitorwait_untilstep that hasn’t finished),completed,failed,skipped(passed over when the run jumped to a goal or ended early) orcanceled.outcome: the result, for example the latest status of the email asend_emailstep sent (delivered,loaded,clicked…),skippedfor a marketing email to an unsubscribed contact,matchedornot_matchedfor a condition,matchedortimeoutfor await_until, orreachedornot_reachedfor a goal.data: the step’s result. For examplesend_emailreturns{ "result": "email_queued", "email_oid": "em_…", "to": "…" },conditionreturns{ "result": true, "branch": "yes" }, and failed steps return{ "error": "…" }.started_at,completed_at,created_at.
waitsobject[]The wait_until (event or condition) and goal waits of the run: id, step_key, kind (event or condition), event, status (waiting, matched, timed_out or canceled), timeout_at, created_at and resolved_at.
Returns 404 if the automation or the run doesn’t exist.
{
"data": {
"id": "aur_3q6GdFk2eRSG093grI0v9e6REu8",
"automation_id": "aut_3xqC9YD79FZZA36uTekWTBO1ghe",
"contact_id": "con_3munwNLaXKUARc6ff9wPtxKVq4A",
"email_id": null,
"event_id": null,
"event": "contact.added_to_audience",
"payload": {
"object": {
"id": "sub_3Fh2pQx9LmZr4Wt7Nc0bVd8KsYe",
"object": "subscriber",
"subscribed": true,
"audience": { "id": "aud_3Cz7WJ9GpvD7P0PQez50xOmkZSV", "name": "Newsletter" },
"contact": { "id": "con_3munwNLaXKUARc6ff9wPtxKVq4A", "email": "ada@example.com" }
}
},
"meta": { "source_event_id": "evt_3Kd8sWq1NzXc5Vb7Mt2LpRy0HgA" },
"status": "running",
"started_at": "2026-10-03T14:12:40.551870+00:00",
"completed_at": null,
"created_at": "2026-10-03T14:12:40.551870+00:00",
"updated_at": "2026-10-03T14:12:40.551870+00:00",
"run_steps": [
{
"step_id": "aus_3V5TZIXXak1SSmGcOmzPOeFaguQ",
"status": "completed",
"data": {
"result": "email_queued",
"outcome": "accepted",
"email_oid": "em_3Cp8cMgPskzB8tIlgUyNJkpDp9O",
"to": "ada@example.com"
},
"started_at": "2026-10-03T14:12:40.702113+00:00",
"completed_at": "2026-10-03T14:12:40.918540+00:00",
"created_at": "2026-10-03T14:12:40.702113+00:00"
},
{
"step_id": "aus_3vM0ORwEXCQcyS7rylfQBQEXjVk",
"status": "waiting",
"data": { "status": "waiting", "delayMs": 86400000, "seconds": 86400 },
"started_at": "2026-10-03T14:12:41.004221+00:00",
"completed_at": null,
"created_at": "2026-10-03T14:12:41.004221+00:00"
}
]
}
}{
"message": "Run not found."
}Cancel a run
Cancels a run that is still running. Steps waiting in a Time delay or Wait until are canceled, no further steps run, and the run’s status becomes canceled. Requires an API key with the full scope.
Canceled runs can’t be resumed. To cancel every run of one contact in the automation, use Remove a contact. To cancel runs automatically, use exit conditions.
/automations/{id}/runs/{run_id}/cancelPath parameters
idstringrequiredaut_…).run_idstringrequiredaur_…).Body parameters
reasonstringdefault: canceled_by_usercancel_reason, up to 200 characters.Returns
Returns the run in data, with status canceled and cancel_reason. Returns 404 if the automation or run doesn’t exist, and 422 if the run isn’t running.
Remove a contact from an automation
Cancels every run of this automation that is still running for one contact, for example from your shop when an order is paid. The runs get the status canceled. Requires an API key with the full scope.
To take contacts out automatically when something happens, use exit conditions instead.
/automations/{id}/exitPath parameters
idstringrequiredaut_…).Body parameters
contact_idstringcon_…). Send this or email.emailstringreasonstringdefault: exited_by_apicancel_reason, up to 200 characters.Returns
dataobjectautomation_id, contact_id, and canceled_runs, the IDs of the runs canceled. Empty when the contact had no running runs.messagestringCanceled 1 run.Returns 400 without contact_id or email, and 404 if the automation or contact doesn’t exist.
List a contact’s runs
Returns the latest 100 runs of one contact across all automations, newest first, each with the automation’s name. Requires an API key with the full scope.
/automations/runsQuery parameters
contact_idstringcon_…). Send this or email.emailstringstatusstringrunning, completed, failed or canceled.Returns
dataobject[]automation_name.Returns 400 without contact_id or email, and 404 if the contact doesn’t exist.
Retrieve statistics
Returns counts for every step of an automation, keyed by step key. Requires an API key with the full scope.
/automations/{id}/statsPath parameters
idstringrequiredaut_…).Query parameters
sincestring2026-10-01T00:00:00Z.untilstringrun_ids[]stringaur_…). Repeat the parameter for several runs.Returns
Returns data, an object with one entry per step key. Steps no run has reached have a total of 0.
totalintegerby_statusobjectrunning, waiting, completed, failed.by_outcomeobjectsend_email and forward_email: accepted, then the email’s latest state (delivered, loaded, clicked, bounced, failed, complained, unsubscribed, canceled). condition: matched, not_matched. experiment: the chosen variant key. call_webhook: 2xx, 4xx, 5xx, timeout, network_error. Failed steps: error.funnelobjectsend_email steps. Cumulative counts: accepted includes every email that got further, delivered includes loaded and clicked emails, and loaded includes clicked ones. bounced, failed, complained and unsubscribed are plain counts.{
"data": {
"joined": { "total": 0, "by_status": {}, "by_outcome": {} },
"welcome_email": {
"total": 412,
"by_status": { "completed": 409, "failed": 3 },
"by_outcome": { "delivered": 251, "loaded": 98, "clicked": 41, "bounced": 19, "error": 3 },
"funnel": {
"accepted": 390,
"delivered": 390,
"loaded": 139,
"clicked": 41,
"bounced": 19,
"failed": 0,
"complained": 0,
"unsubscribed": 0
}
},
"wait_1_day": {
"total": 409,
"by_status": { "completed": 352, "waiting": 57 },
"by_outcome": {}
},
"is_free_user": {
"total": 352,
"by_status": { "completed": 352 },
"by_outcome": { "matched": 270, "not_matched": 82 }
},
"upgrade_tips": {
"total": 270,
"by_status": { "completed": 270 },
"by_outcome": { "accepted": 12, "delivered": 180, "loaded": 61, "clicked": 17 },
"funnel": {
"accepted": 270,
"delivered": 258,
"loaded": 78,
"clicked": 17,
"bounced": 0,
"failed": 0,
"complained": 0,
"unsubscribed": 0
}
},
"done": {
"total": 82,
"by_status": { "completed": 82 },
"by_outcome": {}
}
}
}{
"message": "Automation not found."
}Retrieve step statistics
Returns the counts for one step of an automation. Requires an API key with the full scope. The fields are the same as in Retrieve statistics.
/automations/{id}/steps/{step_key}/statsPath parameters
idstringrequiredaut_…).step_keystringrequiredkey, for example welcome_email.Query parameters
sincestringuntilstringReturns
Returns data with total, by_status, by_outcome and, for send_email steps, funnel. Returns 404 if the automation or the step key doesn’t exist.
{
"data": {
"total": 412,
"by_status": { "completed": 409, "failed": 3 },
"by_outcome": { "delivered": 251, "loaded": 98, "clicked": 41, "bounced": 19, "error": 3 },
"funnel": {
"accepted": 390,
"delivered": 390,
"loaded": 139,
"clicked": 41,
"bounced": 19,
"failed": 0,
"complained": 0,
"unsubscribed": 0
}
}
}{
"message": "Step not found."
}