Pushes
A push adds and updates every resource it names and touches nothing it does not name: a key absent from the body stays as the app holds it, an events list present replaces the app’s and absent leaves it, purposes present are upserted. It files a new version of every automation and template whose content changed; a key that did not change gets none. The plan names every resource the push would change whose current content was written in the dashboard or over the API as a conflict, and the push is refused over one unless force is set. A push turns nothing on: automation status is untouched. Deleting is always an explicit call.
POST /v1/pushes/plan
Section titled “POST /v1/pushes/plan”What a push of these resources would change
Takes the body pushes.create takes, writes nothing, and refuses what that push would refuse. A resource with invalid fields answers validation_failed naming each one in error.issues by its kind, key and field.
Auth: Session token, or an org pipeline key.
Request body
{ "data": {}}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
any | yes |
Response 200
{ "data": { "appId": "string", "entries": [ { "kind": "automation", "key": "string", "change": "added", "fields": [ "string" ] } ], "drift": [ { "kind": "automation", "key": "string", "reason": "not-in-tree" } ], "conflicts": [ { "kind": "automation", "key": "string", "updatedAt": "2026-01-15T09:30:00.000Z" } ], "events": { "added": [ "string" ], "removed": [ "string" ], "listed": true }, "purposes": { "added": [ "string" ], "changed": [ "string" ] } }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.appId |
string | yes | length 0–200 |
data.entries |
object[] | yes | |
data.entries[] |
object | yes | |
data.entries[].kind |
“automation” | “segment” | “template” | yes | |
data.entries[].key |
string | yes | |
data.entries[].change |
“added” | “changed” | “unchanged” | yes | |
data.entries[].fields |
string[] | yes | |
data.drift |
object[] | yes | |
data.drift[] |
object | yes | |
data.drift[].kind |
“automation” | “segment” | “template” | yes | |
data.drift[].key |
string | yes | |
data.drift[].reason |
“not-in-tree” | “on-hold” | yes | |
data.conflicts |
object[] | yes | |
data.conflicts[] |
object | yes | |
data.conflicts[].kind |
“automation” | “segment” | “template” | “events” | “purpose” | yes | |
data.conflicts[].key |
string | yes | |
data.conflicts[].updatedAt |
string (date-time) | yes | |
data.events |
object | null | yes | |
data.events.added |
string[] | yes | |
data.events.removed |
string[] | yes | |
data.events.listed |
boolean | yes | |
data.purposes |
object | null | yes | |
data.purposes.added |
string[] | yes | |
data.purposes.changed |
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/pushes
Section titled “POST /v1/pushes”Push resources to an app
Applies the automations, templates, segments, event list and purposes in the body to the app in one transaction and files a version of every automation and template whose content changed. A resource the body leaves out is left as it is. Refused with conflict when a resource it would change was last written in the dashboard or over the API, unless force is true; refused when an automation’s from address uses a domain your organization has not verified. Resources with invalid fields are refused with validation_failed, which names every one in error.issues by its kind, key and field, and the first in the message, e.g. templates["welcome"].template.subject: a subject is required. A push that prunes (prune: true) takes an organization admin’s session.
Auth: Session token, or an org pipeline key.
Request body
{ "data": {}}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
any | yes |
Response 201
{ "data": { "appId": "string", "versions": [ { "id": "string", "orgId": "string", "key": "string", "kind": "automation", "appId": "string", "source": "push", "moduleDigest": "string", "manifest": { "key": "string", "tags": {}, "trigger": { "event": "string" }, "purpose": "string", "enrollment": { "cooldown": "string" }, "description": "string", "from": "string", "definition": { "trigger": { "event": "string" }, "purpose": "marketing", "from": "string", "tags": {}, "description": "string", "enrollment": { "cooldown": "string" }, "steps": [ { "id": "string", "type": "sleep", "duration": "string" } ] }, "spine": [ { "name": "sleep", "detail": "string", "timeout": "string", "steps": [], "otherwise": [] } ], "bundle": "string" }, "protocol": 42, "status": "compiling", "pushedAt": "2026-01-15T09:30:00.000Z", "compiledAt": "2026-01-15T09:30:00.000Z", "error": "string" } ], "prunedSegments": [ "string" ] }, "meta": { "requestId": "string", "nextCursor": "string", "hasMore": true, "warnings": [ { "code": "string", "message": "string", "suggestion": "string" } ] }}| Field | Type | Required | Constraints |
|---|---|---|---|
data |
object | yes | |
data.appId |
string | yes | length 0–200 |
data.versions |
object[] | yes | |
data.versions[] |
object | yes | |
data.versions[].id |
string | yes | |
data.versions[].orgId |
string | yes | |
data.versions[].key |
string | yes | |
data.versions[].kind |
“automation” | “template” | yes | |
data.versions[].appId |
string | yes | |
data.versions[].source |
“push” | “api” | yes | |
data.versions[].moduleDigest |
string | null | yes | |
data.versions[].manifest |
object | object | yes | |
data.versions[].protocol |
integer | yes | -2147483648 ≤ x ≤ 2147483647 |
data.versions[].status |
“compiling” | “ready” | “failed” | yes | |
data.versions[].pushedAt |
string (date-time) | yes | |
data.versions[].compiledAt |
string (date-time) | null | yes | |
data.versions[].error |
string | null | yes | |
data.prunedSegments |
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.