Developer Platform
Docs chevron_right Developer Platform chevron_right Automations API

Automations API

Automations are visual flows of triggers, conditions, and actions. The API covers the full lifecycle: build a flow from a JSON graph or a natural-language prompt, read and edit it (every change appears live in the flow editor), activate it, and manage individual contact runs. Requires the automations scope.

The graph format

Flows travel in one canonical shape, used identically by every endpoint below (and by the OpenAPI spec and SDK). The trigger is its own object; every node names its parent, and children of a condition carry a branch of yes or no:

{
  "trigger": { "key": "welcome-new-subscriber", "options": {} },
  "nodes": [
    { "id": "n_wait0001", "type": "wait", "parent_id": "trigger", "options": { "duration": "PT1H" } },
    { "id": "n_send0001", "type": "send", "parent_id": "n_wait0001", "options": { "title": "Welcome email" } },
    { "id": "n_cond0001", "type": "condition", "parent_id": "n_send0001",
      "options": { "kind": "open", "timeout": "P3D" } },
    { "id": "n_yes00001", "type": "send", "parent_id": "n_cond0001", "branch": "yes", "options": {} }
  ]
}

Node types are send, wait (ISO 8601 duration, e.g. PT1H, P3D), wait_until, condition (kind: open or click, plus a timeout), operation (kind: tag, remove_tag, copy, move, update), and webhook. Trigger keys match the trigger catalog (welcome-new-subscriber, tag-based, api-3-0, …). Responses also include an issues array — the same “ready to run?” findings the editor shows (for example a send node without an email configured). Replaying a run from a specific step is planned for a later phase.

Flow endpoints

MethodEndpointDescription
GET/automationsPaginated index with status, trigger key, and node count.
POST/automationsCreate from a graph (name, list_uid, trigger, nodes) or compose with AI (name, list_uid, prompt ≤4000 chars) — exactly one of the two. The prompt branch consumes AI credits and the ai rate-limit class.
GET/automations/{uid}Full normalized graph plus editor issues.
PATCH/automations/{uid}Atomic operation batch: {"operations": [{"op": "add_node" | "update_node" | "delete_node" | "update_trigger", ...}]}. One invalid operation rejects the whole batch with 422 — nothing is applied.
DELETE/automations/{uid}Cascade delete (flow, emails, runs). 409 while active unless ?force=1.
POST/automations/{uid}/activateActivate (idempotent). 422 until a trigger is configured.
POST/automations/{uid}/deactivateDeactivate (idempotent).
# Create a welcome flow from a graph
curl -X POST https://emailflow.ai/api/v1/automations \
  -H "Authorization: Bearer efa_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Welcome series",
    "list_uid": "ab12cd34ef",
    "trigger": { "key": "welcome-new-subscriber", "options": {} },
    "nodes": [
      { "id": "n_wait0001", "type": "wait", "parent_id": "trigger", "options": { "duration": "PT1H" } },
      { "id": "n_send0001", "type": "send", "parent_id": "n_wait0001", "options": { "title": "Welcome email" } }
    ]
  }'

# ...or describe it and let the AI compose the graph
curl -X POST https://emailflow.ai/api/v1/automations \
  -H "Authorization: Bearer efa_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Welcome series",
    "list_uid": "ab12cd34ef",
    "prompt": "Welcome new subscribers, wait an hour, then send a welcome email"
  }'

# Edit atomically: re-point the trigger and grow a branch in one batch
curl -X PATCH https://emailflow.ai/api/v1/automations/{uid} \
  -H "Authorization: Bearer efa_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "operations": [
      { "op": "update_trigger", "key": "tag-based", "options": { "tags": ["vip"] } },
      { "op": "add_node", "type": "condition", "parent_id": "n_send0001",
        "options": { "kind": "open", "timeout": "P3D" }, "as": "check" },
      { "op": "add_node", "type": "send", "parent_id": "$check", "branch": "yes",
        "options": { "title": "Openers follow-up" } }
    ]
  }'

Runs

A run is one contact's journey through one automation: where it is (current_node_id), its status (running, waiting, completed, failed, cancelled), and when it wakes next (scheduled_at). Fire a run to enroll a contact from your own system — firing is idempotent per contact (re-firing returns the existing run with 200 instead of enrolling twice). Cancelling is terminal and irreversible: the run keeps its position for audit but never advances again. An optional payload object is stored on the run for audit.

MethodEndpointDescription
GET/automations/{uid}/runsPaginated runs, newest first. Filter with status=.
POST/automations/{uid}/runsFire for a contact: email or subscriber_uid, optional payload. The automation must be active (409 otherwise); the contact must be subscribed on the automation's list.
PATCH/automation-runs/{uid}{"action": "cancel"} — halt the run permanently. Idempotent; completed or failed runs return 409.
# Fire for one contact
curl -X POST https://emailflow.ai/api/v1/automations/{uid}/runs \
  -H "Authorization: Bearer efa_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "alice@example.com", "payload": { "order_id": "ord_42" } }'

# Response (201)
{
  "data": {
    "uid": "64a91b2c7de31",
    "subscriber_uid": "5f8e2a1c9b3d4",
    "email": "alice@example.com",
    "status": "waiting",
    "current_node_id": "n_wait0001",
    "scheduled_at": "2026-06-12T14:30:00+00:00",
    "error": null,
    "created_at": "2026-06-12T13:30:00+00:00",
    "updated_at": "2026-06-12T13:30:00+00:00"
  }
}

# Cancel it
curl -X PATCH https://emailflow.ai/api/v1/automation-runs/64a91b2c7de31 \
  -H "Authorization: Bearer efa_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "action": "cancel" }'

Deprecated but supported: POST /automations/{uid}/execute (fires an api-3-0 automation for its whole list, returning {"success": true}) predates the runs API and remains available for existing integrations. New integrations should fire per-contact runs instead.