Automations
An automation is a JSON definition, a trigger and a list of steps (schema: /schemas/automation.json), addressed by its key in its app and running its latest ready version: a key is unique per app, so every route that names one takes appId and a request without it is a 422 naming the parameter. Create and update write a definition the way a push of that one file would. Status decides whether the automation fires, and neither a push nor a write touches it: a key is off until it is turned on. The spine is a display outline derived from the steps. An automation belongs to one app and fires on that app’s events only. A trigger’s event is a pattern in which * matches any sequence of characters, one or a list of them, and a pattern reaches Cowliss’s own system. events only when its literal prefix starts with system.: see Event patterns. Executions are on the Executions page.
GET /v1/automations
Section titled “GET /v1/automations”List the organization’s automations
Auth: Session token.
Query parameters
{ "cursor": "string", "limit": 50, "status": "on", "appId": "string", "q": "string", "tag": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
cursor |
string | no | |
limit |
integer | no | default 50; 1 ≤ x ≤ 100 |
status |
“on” | “off” | “paused” | no | |
appId |
string | no | length 0–200 |
q |
string | no | length 0–200 |
tag |
string | no | length 0–50 |
Response 200
{ "data": [ { "orgId": "string", "key": "string", "appId": "string", "tags": [ "string" ], "trigger": { "event": "string" }, "purpose": "string", "description": "string", "spine": [ { "name": "sleep", "detail": "string", "timeout": "string", "steps": [], "otherwise": [] } ], "status": "on", "source": "push", "createdAt": "2026-01-15T09:30:00.000Z", "updatedAt": "2026-01-15T09:30:00.000Z", "latestVersion": { "id": "string", "status": "compiling", "appId": "string", "error": "string", "protocol": 42, "source": "push", "pushedAt": "2026-01-15T09:30:00.000Z", "bundle": "string" } } ], "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object[] | yes | |
data[] |
object | yes | |
data[].orgId |
string | yes | |
data[].key |
string | yes | |
data[].appId |
string | yes | |
data[].tags |
string[] | yes | |
data[].trigger |
object | object | object | yes | |
data[].purpose |
string | yes | |
data[].description |
string | null | yes | |
data[].spine |
object[] | yes | |
data[].spine[] |
object | yes | |
data[].spine[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data[].spine[].detail |
string | no | length 0–200 |
data[].spine[].timeout |
string | no | length 0–50 |
data[].spine[].steps |
object[] | no | |
data[].spine[].steps[] |
object | no | |
data[].spine[].steps[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data[].spine[].steps[].detail |
string | no | length 0–200 |
data[].spine[].steps[].timeout |
string | no | length 0–50 |
data[].spine[].steps[].steps |
object[] | no | |
data[].spine[].steps[].otherwise |
object[] | no | |
data[].spine[].otherwise |
object[] | no | |
data[].spine[].otherwise[] |
object | no | |
data[].spine[].otherwise[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data[].spine[].otherwise[].detail |
string | no | length 0–200 |
data[].spine[].otherwise[].timeout |
string | no | length 0–50 |
data[].spine[].otherwise[].steps |
object[] | no | |
data[].spine[].otherwise[].otherwise |
object[] | no | |
data[].status |
“on” | “off” | “paused” | yes | |
data[].source |
“push” | “api” | yes | |
data[].createdAt |
string (date-time) | yes | |
data[].updatedAt |
string (date-time) | yes | |
data[].latestVersion |
object | null | yes | |
data[].latestVersion.id |
string | yes | |
data[].latestVersion.status |
“compiling” | “ready” | “failed” | yes | |
data[].latestVersion.appId |
string | yes | |
data[].latestVersion.error |
string | null | yes | |
data[].latestVersion.protocol |
integer | yes | -2147483648 ≤ x ≤ 2147483647 |
data[].latestVersion.source |
“push” | “api” | yes | |
data[].latestVersion.pushedAt |
string (date-time) | yes | |
data[].latestVersion.bundle |
string | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
POST /v1/automations
Section titled “POST /v1/automations”Create an automation from its definition
Admin only. Files the definition exactly as cow push files an automations/<key>.json file, and the automation starts off. A key the app already holds is refused: change that automation with automations.update instead.
Auth: Session token.
Request body
{ "data": { "appId": "string", "key": "string", "definition": { "trigger": { "event": "string" }, "purpose": "marketing", "from": "string", "tags": [], "description": "string", "enrollment": { "cooldown": "string" }, "steps": [ { "id": "string", "type": "sleep", "duration": "string" } ] } }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.appId |
string | yes | |
data.key |
string | yes | length 0–64 |
data.definition |
object | yes | |
data.definition.trigger |
object | object | object | yes | |
data.definition.purpose |
“marketing” | “transactional” | yes | |
data.definition.from |
string | yes | length 0–200 |
data.definition.tags |
string[] | no | default []; items 0–20 |
data.definition.description |
string | no | length 0–500 |
data.definition.enrollment |
object | no | |
data.definition.enrollment.cooldown |
string | integer | yes | |
data.definition.steps |
one of[] | yes | |
data.definition.steps[] |
one of | yes | |
data.definition.steps[]〈sleep〉 |
object | yes | |
data.definition.steps[]〈sleep〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈sleep〉.type |
“sleep” | yes | |
data.definition.steps[]〈sleep〉.duration |
string | integer | yes | |
data.definition.steps[]〈waitForEvent〉 |
object | yes | |
data.definition.steps[]〈waitForEvent〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈waitForEvent〉.type |
“waitForEvent” | yes | |
data.definition.steps[]〈waitForEvent〉.event |
string | string[] | yes | |
data.definition.steps[]〈waitForEvent〉.timeout |
string | integer | yes | |
data.definition.steps[]〈sendEmail〉 |
object | yes | |
data.definition.steps[]〈sendEmail〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈sendEmail〉.type |
“sendEmail” | yes | |
data.definition.steps[]〈sendEmail〉.template |
string | yes | length 0–64 |
data.definition.steps[]〈sendEmail〉.props |
record<string, any> | no | default {} |
data.definition.steps[]〈sendEmail〉.from |
string | no | length 0–200 |
data.definition.steps[]〈sendWebhook〉 |
object | yes | |
data.definition.steps[]〈sendWebhook〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈sendWebhook〉.type |
“sendWebhook” | yes | |
data.definition.steps[]〈sendWebhook〉.webhook |
string | no | length 1–∞ |
data.definition.steps[]〈sendWebhook〉.destination |
string | no | length 1–∞ |
data.definition.steps[]〈sendWebhook〉.payload |
record<string, any> | no | default {} |
data.definition.steps[]〈branch〉 |
object | yes | |
data.definition.steps[]〈branch〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈branch〉.type |
“branch” | yes | |
data.definition.steps[]〈branch〉.if |
string | number | boolean | string | number | boolean | AutomationRule[] | record<string, any> | null[] | record<string, any> | null | yes | |
data.definition.steps[]〈branch〉.then |
one of[] | yes | |
data.definition.steps[]〈branch〉.then[] |
one of | yes | |
data.definition.steps[]〈branch〉.else |
one of[] | no | |
data.definition.steps[]〈branch〉.else[] |
one of | no | |
data.definition.steps[]〈exit〉 |
object | yes | |
data.definition.steps[]〈exit〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈exit〉.type |
“exit” | yes | |
data.definition.steps[]〈restart〉 |
object | yes | |
data.definition.steps[]〈restart〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈restart〉.type |
“restart” | yes |
Response 201
{ "data": { "orgId": "string", "key": "string", "appId": "string", "tags": [ "string" ], "trigger": { "event": "string" }, "purpose": "string", "description": "string", "spine": [ { "name": "sleep", "detail": "string", "timeout": "string", "steps": [], "otherwise": [] } ], "status": "on", "source": "push", "createdAt": "2026-01-15T09:30:00.000Z", "updatedAt": "2026-01-15T09:30:00.000Z", "latestVersion": { "id": "string", "status": "compiling", "appId": "string", "error": "string", "protocol": 42, "source": "push", "pushedAt": "2026-01-15T09:30:00.000Z", "bundle": "string" } }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.orgId |
string | yes | |
data.key |
string | yes | |
data.appId |
string | yes | |
data.tags |
string[] | yes | |
data.trigger |
object | object | object | yes | |
data.purpose |
string | yes | |
data.description |
string | null | yes | |
data.spine |
object[] | yes | |
data.spine[] |
object | yes | |
data.spine[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data.spine[].detail |
string | no | length 0–200 |
data.spine[].timeout |
string | no | length 0–50 |
data.spine[].steps |
object[] | no | |
data.spine[].steps[] |
object | no | |
data.spine[].steps[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data.spine[].steps[].detail |
string | no | length 0–200 |
data.spine[].steps[].timeout |
string | no | length 0–50 |
data.spine[].steps[].steps |
object[] | no | |
data.spine[].steps[].steps[] |
object | no | |
data.spine[].steps[].otherwise |
object[] | no | |
data.spine[].steps[].otherwise[] |
object | no | |
data.spine[].otherwise |
object[] | no | |
data.spine[].otherwise[] |
object | no | |
data.spine[].otherwise[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data.spine[].otherwise[].detail |
string | no | length 0–200 |
data.spine[].otherwise[].timeout |
string | no | length 0–50 |
data.spine[].otherwise[].steps |
object[] | no | |
data.spine[].otherwise[].steps[] |
object | no | |
data.spine[].otherwise[].otherwise |
object[] | no | |
data.spine[].otherwise[].otherwise[] |
object | no | |
data.status |
“on” | “off” | “paused” | yes | |
data.source |
“push” | “api” | yes | |
data.createdAt |
string (date-time) | yes | |
data.updatedAt |
string (date-time) | yes | |
data.latestVersion |
object | null | yes | |
data.latestVersion.id |
string | yes | |
data.latestVersion.status |
“compiling” | “ready” | “failed” | yes | |
data.latestVersion.appId |
string | yes | |
data.latestVersion.error |
string | null | yes | |
data.latestVersion.protocol |
integer | yes | -2147483648 ≤ x ≤ 2147483647 |
data.latestVersion.source |
“push” | “api” | yes | |
data.latestVersion.pushedAt |
string (date-time) | yes | |
data.latestVersion.bundle |
string | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
PATCH /v1/automations/{key}
Section titled “PATCH /v1/automations/{key}”Replace an automation’s definition
Admin only. Files the definition as a new version, the way a push of the changed file does. Runs already in flight finish on the version they started on; new runs take this one.
Auth: Session token.
Query parameters
{ "appId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
appId |
string | yes |
Request body
{ "data": { "definition": { "trigger": { "event": "string" }, "purpose": "marketing", "from": "string", "tags": [], "description": "string", "enrollment": { "cooldown": "string" }, "steps": [ { "id": "string", "type": "sleep", "duration": "string" } ] } }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.definition |
object | yes | |
data.definition.trigger |
object | object | object | yes | |
data.definition.purpose |
“marketing” | “transactional” | yes | |
data.definition.from |
string | yes | length 0–200 |
data.definition.tags |
string[] | no | default []; items 0–20 |
data.definition.description |
string | no | length 0–500 |
data.definition.enrollment |
object | no | |
data.definition.enrollment.cooldown |
string | integer | yes | |
data.definition.steps |
one of[] | yes | |
data.definition.steps[] |
one of | yes | |
data.definition.steps[]〈sleep〉 |
object | yes | |
data.definition.steps[]〈sleep〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈sleep〉.type |
“sleep” | yes | |
data.definition.steps[]〈sleep〉.duration |
string | integer | yes | |
data.definition.steps[]〈waitForEvent〉 |
object | yes | |
data.definition.steps[]〈waitForEvent〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈waitForEvent〉.type |
“waitForEvent” | yes | |
data.definition.steps[]〈waitForEvent〉.event |
string | string[] | yes | |
data.definition.steps[]〈waitForEvent〉.timeout |
string | integer | yes | |
data.definition.steps[]〈sendEmail〉 |
object | yes | |
data.definition.steps[]〈sendEmail〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈sendEmail〉.type |
“sendEmail” | yes | |
data.definition.steps[]〈sendEmail〉.template |
string | yes | length 0–64 |
data.definition.steps[]〈sendEmail〉.props |
record<string, any> | no | default {} |
data.definition.steps[]〈sendEmail〉.from |
string | no | length 0–200 |
data.definition.steps[]〈sendWebhook〉 |
object | yes | |
data.definition.steps[]〈sendWebhook〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈sendWebhook〉.type |
“sendWebhook” | yes | |
data.definition.steps[]〈sendWebhook〉.webhook |
string | no | length 1–∞ |
data.definition.steps[]〈sendWebhook〉.destination |
string | no | length 1–∞ |
data.definition.steps[]〈sendWebhook〉.payload |
record<string, any> | no | default {} |
data.definition.steps[]〈branch〉 |
object | yes | |
data.definition.steps[]〈branch〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈branch〉.type |
“branch” | yes | |
data.definition.steps[]〈branch〉.if |
string | number | boolean | string | number | boolean | AutomationRule[] | record<string, any> | null[] | record<string, any> | null | yes | |
data.definition.steps[]〈branch〉.then |
one of[] | yes | |
data.definition.steps[]〈branch〉.then[] |
one of | yes | |
data.definition.steps[]〈branch〉.else |
one of[] | no | |
data.definition.steps[]〈branch〉.else[] |
one of | no | |
data.definition.steps[]〈exit〉 |
object | yes | |
data.definition.steps[]〈exit〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈exit〉.type |
“exit” | yes | |
data.definition.steps[]〈restart〉 |
object | yes | |
data.definition.steps[]〈restart〉.id |
string | yes | length 0–64 |
data.definition.steps[]〈restart〉.type |
“restart” | yes |
Response 200
{ "data": { "orgId": "string", "key": "string", "appId": "string", "tags": [ "string" ], "trigger": { "event": "string" }, "purpose": "string", "description": "string", "spine": [ { "name": "sleep", "detail": "string", "timeout": "string", "steps": [], "otherwise": [] } ], "status": "on", "source": "push", "createdAt": "2026-01-15T09:30:00.000Z", "updatedAt": "2026-01-15T09:30:00.000Z", "latestVersion": { "id": "string", "status": "compiling", "appId": "string", "error": "string", "protocol": 42, "source": "push", "pushedAt": "2026-01-15T09:30:00.000Z", "bundle": "string" } }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.orgId |
string | yes | |
data.key |
string | yes | |
data.appId |
string | yes | |
data.tags |
string[] | yes | |
data.trigger |
object | object | object | yes | |
data.purpose |
string | yes | |
data.description |
string | null | yes | |
data.spine |
object[] | yes | |
data.spine[] |
object | yes | |
data.spine[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data.spine[].detail |
string | no | length 0–200 |
data.spine[].timeout |
string | no | length 0–50 |
data.spine[].steps |
object[] | no | |
data.spine[].steps[] |
object | no | |
data.spine[].steps[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data.spine[].steps[].detail |
string | no | length 0–200 |
data.spine[].steps[].timeout |
string | no | length 0–50 |
data.spine[].steps[].steps |
object[] | no | |
data.spine[].steps[].steps[] |
object | no | |
data.spine[].steps[].otherwise |
object[] | no | |
data.spine[].steps[].otherwise[] |
object | no | |
data.spine[].otherwise |
object[] | no | |
data.spine[].otherwise[] |
object | no | |
data.spine[].otherwise[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data.spine[].otherwise[].detail |
string | no | length 0–200 |
data.spine[].otherwise[].timeout |
string | no | length 0–50 |
data.spine[].otherwise[].steps |
object[] | no | |
data.spine[].otherwise[].steps[] |
object | no | |
data.spine[].otherwise[].otherwise |
object[] | no | |
data.spine[].otherwise[].otherwise[] |
object | no | |
data.status |
“on” | “off” | “paused” | yes | |
data.source |
“push” | “api” | yes | |
data.createdAt |
string (date-time) | yes | |
data.updatedAt |
string (date-time) | yes | |
data.latestVersion |
object | null | yes | |
data.latestVersion.id |
string | yes | |
data.latestVersion.status |
“compiling” | “ready” | “failed” | yes | |
data.latestVersion.appId |
string | yes | |
data.latestVersion.error |
string | null | yes | |
data.latestVersion.protocol |
integer | yes | -2147483648 ≤ x ≤ 2147483647 |
data.latestVersion.source |
“push” | “api” | yes | |
data.latestVersion.pushedAt |
string (date-time) | yes | |
data.latestVersion.bundle |
string | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/automations/{key}
Section titled “GET /v1/automations/{key}”Inspect one automation
Auth: Session token.
Query parameters
{ "appId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
appId |
string | yes |
Response 200
{ "data": { "orgId": "string", "key": "string", "appId": "string", "tags": [ "string" ], "trigger": { "event": "string" }, "purpose": "string", "description": "string", "spine": [ { "name": "sleep", "detail": "string", "timeout": "string", "steps": [], "otherwise": [] } ], "status": "on", "source": "push", "createdAt": "2026-01-15T09:30:00.000Z", "updatedAt": "2026-01-15T09:30:00.000Z", "latestVersion": { "id": "string", "status": "compiling", "appId": "string", "error": "string", "protocol": 42, "source": "push", "pushedAt": "2026-01-15T09:30:00.000Z", "bundle": "string" }, "segment": { "id": "string", "orgId": "string", "appId": "string", "name": "string", "description": "string", "tags": [ "string" ], "definition": { "match": "all", "predicates": [ { "kind": "trait", "name": "string", "op": "eq", "value": {} } ] }, "source": "push", "automationKey": "string", "createdAt": "2026-01-15T09:30:00.000Z", "updatedAt": "2026-01-15T09:30:00.000Z", "memberCount": 42 } }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.orgId |
string | yes | |
data.key |
string | yes | |
data.appId |
string | yes | |
data.tags |
string[] | yes | |
data.trigger |
object | object | object | yes | |
data.purpose |
string | yes | |
data.description |
string | null | yes | |
data.spine |
object[] | yes | |
data.spine[] |
object | yes | |
data.spine[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data.spine[].detail |
string | no | length 0–200 |
data.spine[].timeout |
string | no | length 0–50 |
data.spine[].steps |
object[] | no | |
data.spine[].steps[] |
object | no | |
data.spine[].steps[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data.spine[].steps[].detail |
string | no | length 0–200 |
data.spine[].steps[].timeout |
string | no | length 0–50 |
data.spine[].steps[].steps |
object[] | no | |
data.spine[].steps[].steps[] |
object | no | |
data.spine[].steps[].otherwise |
object[] | no | |
data.spine[].steps[].otherwise[] |
object | no | |
data.spine[].otherwise |
object[] | no | |
data.spine[].otherwise[] |
object | no | |
data.spine[].otherwise[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data.spine[].otherwise[].detail |
string | no | length 0–200 |
data.spine[].otherwise[].timeout |
string | no | length 0–50 |
data.spine[].otherwise[].steps |
object[] | no | |
data.spine[].otherwise[].steps[] |
object | no | |
data.spine[].otherwise[].otherwise |
object[] | no | |
data.spine[].otherwise[].otherwise[] |
object | no | |
data.status |
“on” | “off” | “paused” | yes | |
data.source |
“push” | “api” | yes | |
data.createdAt |
string (date-time) | yes | |
data.updatedAt |
string (date-time) | yes | |
data.latestVersion |
object | null | yes | |
data.latestVersion.id |
string | yes | |
data.latestVersion.status |
“compiling” | “ready” | “failed” | yes | |
data.latestVersion.appId |
string | yes | |
data.latestVersion.error |
string | null | yes | |
data.latestVersion.protocol |
integer | yes | -2147483648 ≤ x ≤ 2147483647 |
data.latestVersion.source |
“push” | “api” | yes | |
data.latestVersion.pushedAt |
string (date-time) | yes | |
data.latestVersion.bundle |
string | yes | |
data.segment |
object | null | yes | |
data.segment.id |
string | yes | |
data.segment.orgId |
string | yes | |
data.segment.appId |
string | yes | |
data.segment.name |
string | yes | |
data.segment.description |
string | null | yes | |
data.segment.tags |
string[] | yes | |
data.segment.definition |
object | yes | |
data.segment.definition.match |
“all” | “any” | yes | default "all" |
data.segment.definition.predicates |
array of variants | yes | items 1–∞ |
data.segment.definition.predicates[] |
one of | yes | |
data.segment.definition.predicates[]〈trait〉 |
object | yes | |
data.segment.definition.predicates[]〈trait〉.kind |
“trait” | yes | |
data.segment.definition.predicates[]〈trait〉.name |
string | yes | length 1–200 |
data.segment.definition.predicates[]〈trait〉.op |
“eq” | “neq” | “gt” | “gte” | “lt” | “lte” | “exists” | “notExists” | “contains” | “before” | “after” | yes | |
data.segment.definition.predicates[]〈trait〉.value |
any | no | |
data.segment.definition.predicates[]〈event〉 |
object | yes | |
data.segment.definition.predicates[]〈event〉.kind |
“event” | yes | |
data.segment.definition.predicates[]〈event〉.name |
string | string[] | yes | |
data.segment.definition.predicates[]〈event〉.op |
“performed” | “notPerformed” | yes | |
data.segment.definition.predicates[]〈event〉.atLeast |
integer | yes | default 1; 1 ≤ x ≤ ∞ |
data.segment.definition.predicates[]〈event〉.withinDays |
integer | no | 1 ≤ x ≤ ∞ |
data.segment.source |
“push” | “api” | yes | |
data.segment.automationKey |
string | null | yes | |
data.segment.createdAt |
string (date-time) | yes | |
data.segment.updatedAt |
string (date-time) | yes | |
data.segment.memberCount |
integer | yes | 0 ≤ x ≤ ∞ |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/automations/{key}/stats
Section titled “GET /v1/automations/{key}/stats”Automation operational stats
Auth: Session token.
Query parameters
{ "appId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
appId |
string | yes |
Response 200
{ "data": { "executions": { "entered": 42, "active": 42, "completed": 42, "failed": 42, "lastStartedAt": "2026-01-15T09:30:00.000Z" }, "deliveries": { "byStatus": { "sent": 42 }, "sentLast30d": 42 } }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.executions |
object | yes | |
data.executions.entered |
integer | yes | |
data.executions.active |
integer | yes | |
data.executions.completed |
integer | yes | |
data.executions.failed |
integer | yes | |
data.executions.lastStartedAt |
string (date-time) | null | yes | |
data.deliveries |
object | yes | |
data.deliveries.byStatus |
record<sent | delivered | bounced | complained | failed | skipped_disabled | skipped_paused | skipped_suspended | skipped_suppressed | skipped_quota | skipped_unknown_purpose | skipped_frequency_cap | skipped_consent | skipped_domain | skipped_ssrf | skipped_spam | would_send | would_skip_disabled | would_skip_paused | would_skip_suspended | would_skip_suppressed | would_skip_quota | would_skip_unknown_purpose | would_skip_frequency_cap | would_skip_consent | would_skip_domain | would_skip_ssrf | would_skip_spam, integer> | yes | |
data.deliveries.sentLast30d |
integer | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
POST /v1/automations/status
Section titled “POST /v1/automations/status”Put automations on, off or on hold
Admin only. Turning an automation off lets executions in flight finish, while putting it on hold pauses them before their next step. Turning a held automation on resumes them.
Auth: Session token, or an org pipeline key.
Request body
{ "data": { "keys": [ "string" ], "appId": "string", "status": "on" }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.keys |
string[] | no | items 1–100 |
data.appId |
string | yes | |
data.status |
“on” | “off” | “paused” | yes |
Response 200
{ "data": [ { "orgId": "string", "key": "string", "appId": "string", "tags": [ "string" ], "trigger": { "event": "string" }, "purpose": "string", "description": "string", "spine": [ { "name": "sleep", "detail": "string", "timeout": "string", "steps": [], "otherwise": [] } ], "status": "on", "source": "push", "createdAt": "2026-01-15T09:30:00.000Z", "updatedAt": "2026-01-15T09:30:00.000Z", "latestVersion": { "id": "string", "status": "compiling", "appId": "string", "error": "string", "protocol": 42, "source": "push", "pushedAt": "2026-01-15T09:30:00.000Z", "bundle": "string" }, "enrollment": { "counts": { "scanned": 42, "enrolled": 42, "skipped": { "completed": 42, "cooldown": 42, "running": 42 } }, "url": "string" } } ], "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object[] | yes | |
data[] |
object | yes | |
data[].orgId |
string | yes | |
data[].key |
string | yes | |
data[].appId |
string | yes | |
data[].tags |
string[] | yes | |
data[].trigger |
object | object | object | yes | |
data[].purpose |
string | yes | |
data[].description |
string | null | yes | |
data[].spine |
object[] | yes | |
data[].spine[] |
object | yes | |
data[].spine[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data[].spine[].detail |
string | no | length 0–200 |
data[].spine[].timeout |
string | no | length 0–50 |
data[].spine[].steps |
object[] | no | |
data[].spine[].steps[] |
object | no | |
data[].spine[].steps[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data[].spine[].steps[].detail |
string | no | length 0–200 |
data[].spine[].steps[].timeout |
string | no | length 0–50 |
data[].spine[].steps[].steps |
object[] | no | |
data[].spine[].steps[].otherwise |
object[] | no | |
data[].spine[].otherwise |
object[] | no | |
data[].spine[].otherwise[] |
object | no | |
data[].spine[].otherwise[].name |
“sleep” | “waitForEvent” | “send.email” | “send.webhook” | “profile.get” | “profiles.get” | “restart” | “traits.set” | “traits.unset” | “events.track” | “if” | “loop” | “end” | yes | |
data[].spine[].otherwise[].detail |
string | no | length 0–200 |
data[].spine[].otherwise[].timeout |
string | no | length 0–50 |
data[].spine[].otherwise[].steps |
object[] | no | |
data[].spine[].otherwise[].otherwise |
object[] | no | |
data[].status |
“on” | “off” | “paused” | yes | |
data[].source |
“push” | “api” | yes | |
data[].createdAt |
string (date-time) | yes | |
data[].updatedAt |
string (date-time) | yes | |
data[].latestVersion |
object | null | yes | |
data[].latestVersion.id |
string | yes | |
data[].latestVersion.status |
“compiling” | “ready” | “failed” | yes | |
data[].latestVersion.appId |
string | yes | |
data[].latestVersion.error |
string | null | yes | |
data[].latestVersion.protocol |
integer | yes | -2147483648 ≤ x ≤ 2147483647 |
data[].latestVersion.source |
“push” | “api” | yes | |
data[].latestVersion.pushedAt |
string (date-time) | yes | |
data[].latestVersion.bundle |
string | yes | |
data[].enrollment |
object | null | yes | |
data[].enrollment.counts |
object | null | yes | |
data[].enrollment.counts.scanned |
integer | yes | |
data[].enrollment.counts.enrolled |
integer | yes | |
data[].enrollment.counts.skipped |
object | yes | |
data[].enrollment.counts.skipped.completed |
integer | yes | |
data[].enrollment.counts.skipped.cooldown |
integer | yes | |
data[].enrollment.counts.skipped.running |
integer | yes | |
data[].enrollment.url |
string | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/automations/{key}/waits
Section titled “GET /v1/automations/{key}/waits”Count the executions a hold until a given day would end
Auth: Session token.
Query parameters
{ "before": "2026-01-15T09:30:00.000Z", "appId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
before |
string (date-time) | yes | |
appId |
string | yes |
Response 200
{ "data": { "due": 42, "inFlight": 42 }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.due |
integer | yes | |
data.inFlight |
integer | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
DELETE /v1/automations/{key}
Section titled “DELETE /v1/automations/{key}”Delete an automation
Admin only. Executions already in flight finish, but no new ones start. A push never deletes an automation, so this is the only way to remove one whose file was deleted from your code.
Auth: Session token.
Query parameters
{ "appId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
appId |
string | yes |
Response 200
{ "data": { "deleted": true }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.deleted |
true | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/automations/{key}/enrollment/preview
Section titled “GET /v1/automations/{key}/enrollment/preview”Count who turning this automation on would reach
Auth: Session token.
Query parameters
{ "appId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
appId |
string | yes |
Response 200
{ "data": { "scanned": 42, "enrolled": 42, "skipped": { "completed": 42, "cooldown": 42, "running": 42 } }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.scanned |
integer | yes | |
data.enrolled |
integer | yes | |
data.skipped |
object | yes | |
data.skipped.completed |
integer | yes | |
data.skipped.cooldown |
integer | yes | |
data.skipped.running |
integer | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
POST /v1/automations/{key}/enrollment
Section titled “POST /v1/automations/{key}/enrollment”Enroll the automation’s current members
Admin only. Starts an enrollment for recipients currently in the automation’s segment. If an enrollment is already running, this returns the existing enrollment rather than starting another.
Auth: Session token.
Query parameters
{ "appId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
appId |
string | yes |
Response 202
{ "data": { "scanned": 42, "enrolled": 42, "skipped": { "completed": 42, "cooldown": 42, "running": 42 }, "state": "none" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.scanned |
integer | yes | |
data.enrolled |
integer | yes | |
data.skipped |
object | yes | |
data.skipped.completed |
integer | yes | |
data.skipped.cooldown |
integer | yes | |
data.skipped.running |
integer | yes | |
data.state |
“none” | “running” | “done” | “cancelled” | “stopped” | “failed” | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
GET /v1/automations/{key}/enrollment
Section titled “GET /v1/automations/{key}/enrollment”How far the automation’s enrollment has got
The state is none, with zero counts, when no enrollment has run for the automation.
Auth: Session token.
Query parameters
{ "appId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
appId |
string | yes |
Response 200
{ "data": { "scanned": 42, "enrolled": 42, "skipped": { "completed": 42, "cooldown": 42, "running": 42 }, "state": "none" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.scanned |
integer | yes | |
data.enrolled |
integer | yes | |
data.skipped |
object | yes | |
data.skipped.completed |
integer | yes | |
data.skipped.cooldown |
integer | yes | |
data.skipped.running |
integer | yes | |
data.state |
“none” | “running” | “done” | “cancelled” | “stopped” | “failed” | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
POST /v1/automations/{key}/enrollment/cancel
Section titled “POST /v1/automations/{key}/enrollment/cancel”Stop a running enrollment
Admin only. Recipients already enrolled remain in the automation and their executions continue. Stopping the enrollment only prevents remaining recipients from enrolling.
Auth: Session token.
Query parameters
{ "appId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
appId |
string | yes |
Response 200
{ "data": { "scanned": 42, "enrolled": 42, "skipped": { "completed": 42, "cooldown": 42, "running": 42 }, "state": "none" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.scanned |
integer | yes | |
data.enrolled |
integer | yes | |
data.skipped |
object | yes | |
data.skipped.completed |
integer | yes | |
data.skipped.cooldown |
integer | yes | |
data.skipped.running |
integer | yes | |
data.state |
“none” | “running” | “done” | “cancelled” | “stopped” | “failed” | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.
POST /v1/automations/{key}/dry-run
Section titled “POST /v1/automations/{key}/dry-run”Dry-run an automation against a real user
Admin only. Runs the automation’s latest ready version against the specified profile with sends disabled, delivering no emails or webhooks. The execution proceeds even if the automation is turned off.
Auth: Session token.
Query parameters
{ "appId": "string"}| Field | Type | Required | Constraints |
|---|---|---|---|
appId |
string | yes |
Request body
{ "data": { "profileId": "string" }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.profileId |
string | yes | length 1–∞ |
Response 201
{ "data": { "id": "string", "orgId": "string", "appId": "string", "automationKey": "string", "profileId": "string", "workflowId": "string", "runId": "string", "versionId": "string", "triggerEventId": "string", "status": "running", "step": "string", "timerDeadline": "2026-01-15T09:30:00.000Z", "journalEntries": 42, "error": "string", "startedAt": "2026-01-15T09:30:00.000Z", "updatedAt": "2026-01-15T09:30:00.000Z", "endedAt": "2026-01-15T09:30:00.000Z" }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.id |
string | yes | |
data.orgId |
string | yes | |
data.appId |
string | yes | |
data.automationKey |
string | yes | |
data.profileId |
string | yes | |
data.workflowId |
string | yes | |
data.runId |
string | yes | |
data.versionId |
string | yes | |
data.triggerEventId |
string | null | yes | |
data.status |
“running” | “waiting” | “completed” | “failed” | “cancelled” | yes | |
data.step |
string | null | yes | |
data.timerDeadline |
string (date-time) | null | yes | |
data.journalEntries |
integer | yes | -2147483648 ≤ x ≤ 2147483647 |
data.error |
string | null | yes | |
data.startedAt |
string (date-time) | yes | |
data.updatedAt |
string (date-time) | yes | |
data.endedAt |
string (date-time) | null | yes | |
meta |
object | no | |
meta.requestId |
string | no | |
meta.nextCursor |
string | null | no | |
meta.hasMore |
boolean | no | |
meta.warnings |
object[] | no | |
meta.warnings[] |
object | no | |
meta.warnings[].code |
string | yes | |
meta.warnings[].message |
string | yes | |
meta.warnings[].suggestion |
string | no |
Errors follow the shared envelope and code table.