Automations

email_create_automation

Create an event-triggered automation (welcome flow, weekly engagement sequence, win-back).

write email_create_automation

Create an event-triggered automation (welcome flow, weekly engagement sequence, win-back). ALWAYS created paused — test with email_emit_contact_event, then enable via email_set_automation_status on the human's explicit OK. Attribute-routing: create N automations on the same trigger with different filters + priorities; exclusive=true means first-match-only. triggerFilter accepts a flat array (all AND) OR a nested {op:'and'|'or', conditions:[...]} group for OR logic. SEGMENT-ENTRY trigger: set triggerEvent to "$segment_entered" and triggerFilter to [{path:'event.properties.segmentId', op:'eq', value:'<segmentId>'}] to enroll a contact the moment their attributes make them newly match that segment (still throttled/suppressed — no blast). SCHEDULED trigger: set triggerEvent to "$scheduled" plus triggerSchedule ('daily'|'weekly'|'monthly') and a triggerFilter — a sweep re-evaluates the filter across the audience on that cadence and enrolls whoever matches. $segment_entered is EDGE-triggered (fires on a transition, so it never reaches contacts already in the state); $scheduled is LEVEL-triggered, which is what list hygiene needs. A $scheduled automation REQUIRES a filter and should set allowReenrollment with a cooldown. ENGAGEMENT filters (usable in triggerFilter and branch filters): contact.engagement.emailsSinceEngaged, .daysSinceEngaged, .emailsReceived, .openCount, .clickCount, .openRate, .clickRate (percent), .lastOpenedAt, .lastClickedAt, plus contact.subscriptionStatus. Timestamps compare with within_days / older_than_days (a NUMBER OF DAYS); both are FALSE when the timestamp is null, so use daysSinceEngaged for the never-engaged case — it falls back to days since the first email, which is what stops a sunset rule catching new subscribers. STEP TYPES: 'send' (default — templateSlug), 'delay' (a standalone wait — {delaySeconds} is the pause before the next step; stack them, put one at the end, reorder like any step), 'wait_for_event' (pause until a contact_event arrives or timeoutSeconds elapses), 'branch' (see below), 'set_attribute' (merge {attributes} and/or {addTags}/{removeTags} — tags ARE segment membership), 'webhook' (POST the payload to a public {url}), 'unsubscribe' (opt the contact out of ALL lifecycle mail — transactional still delivers; it ENDS the run as unsubscribed, so no 'exit' step is needed after it, and it is a logged no-op when the project's open/click tracking isn't confirmed on), or 'exit' (end the run here, optionally labeled). Prefer a 'delay' step for waits; a delaySeconds on any other step type is auto-split into a delay step before it. BRANCHES own lanes of child steps that CONVERGE after the branch: yes/no = {filter, yes:[steps...], no:[steps...]} (an empty lane skips straight past); multi = {branches:[{key, label?, filter, steps:[...]}], elseSteps:[...]} — first matching arm wins, everyone else takes the else lane. End a lane early with an 'exit' step; otherwise its contacts continue after the branch. Lanes nest at most two levels. The legacy gate shape {filter, ifMatch, ifNoMatch} ('continue'/'exit') still works.

Parameters

ParameterTypeRequiredDescription
namestringYes
triggerEventstringNo
triggerFilterobject[] | anyNo
triggerScheduledaily | weekly | monthlyNoHow often to re-evaluate triggerFilter across the audience. Only meaningful with triggerEvent '$scheduled', which REQUIRES a triggerFilter.
priorityintegerNo
exclusivebooleanNo
exitEventsstring[]No
exitEventstringNo
allowReenrollmentbooleanNo
reenrollmentCooldownSecondsintegerNo
stepsany[]No
triggerSegmentIdstringNoEnroll a contact the moment their attributes newly match this segment. Sugar for triggerEvent "$segment_entered" + a segmentId filter. Provide this OR triggerEvent, not both.
projectIdstringNoTarget company (project id) — required on company-scoped tools. Call email_list_projects to enumerate companies.

On this page