Skip to main content

Sequences

MethodPathScopeSDK
POST/api/v1/sequencessequences:writesequences.create
GET/api/v1/sequencessequences:writesequences.list
GET/api/v1/sequences/:keysequences:writesequences.get
PUT/api/v1/sequences/:keysequences:writesequences.update
DELETE/api/v1/sequences/:keysequences:writesequences.remove
POST/api/v1/sequences/:key/simulatesequences:writesequences.simulate
POST/api/v1/sequences/:key/enrollsequences:writesequences.enroll
POST/api/v1/sequences/:key/enroll/bulksequences:writesequences.bulkEnroll
note

Sequence reads also require sequences:write — there is no separate read scope.

:key is your own slug, URL-encoded.

POST /api/v1/sequences

Creates a linear sequence from steps; Mailroom builds the flow graph.

FieldTypeNotes
keystringRequired. Unique per project.
namestringRequired. Display name.
stepsarrayRequired. { template, subject?, type?, delayMinutes? }, in order.
statusdraft | active | paused | archivedOnly active sequences enroll and send.
triggerstringEvent type that auto-enrolls (matched against events.track({ type })).
brandstringBrand slug.
frequencyCapobject{ count, windowHours } — ceiling on emails per contact.

201

{ "ok": true, "id": "s1a2…", "key": "onboarding", "steps": 3 }

422 — validation failure or a reason code such as a duplicate key or unknown template.

GET /api/v1/sequences

{
"ok": true,
"sequences": [
{ "id": "s1a2…", "key": "onboarding", "name": "Onboarding",
"status": "active", "trigger": "user.signed_up", "created_at": "2026-07-01T00:00:00.000Z" }
]
}

GET /api/v1/sequences/:key

Returns the full flow graph, spread flat into the envelope:

{
"ok": true,
"key": "onboarding",
"name": "Onboarding",
"status": "active",
"reentry": "once_active",
"cooldownHours": null,
"goal": null,
"frequencyCap": { "count": 3, "windowHours": 168 },
"triggers": [{ "kind": "event", "config": { "type": "user.signed_up" }, "enabled": true }],
"nodes": [
{ "id": "n0", "kind": "trigger" },
{ "id": "n1", "kind": "send_email", "config": { "template": "welcome" } },
{ "id": "n2", "kind": "wait_delay", "config": { "minutes": 1440 } }
],
"edges": [{ "from": "n0", "to": "n1" }, { "from": "n1", "to": "n2" }]
}

Node kinds: trigger, send_email, wait_delay, wait_until, branch, split, update_contact, webhook, subflow, goal, exit. Edges out of a branch or split carry branchKey.

404 not_found — unknown key.

PUT /api/v1/sequences/:key

Replaces the graph atomically — send the whole object, not a patch. The graph is portable: GET from one project, PUT into another.

{ "ok": true, "key": "onboarding", "nodes": 6 }

422 invalid_graph — the message lists every problem found; nothing is applied. 404 not_found — unknown key.

DELETE /api/v1/sequences/:key

Deletes the sequence and its enrollments.

{ "ok": true, "deleted": true }

POST /api/v1/sequences/:key/simulate

Dry-runs one contact through the flow. Sends nothing, creates no enrollment.

FieldTypeNotes
contactIduuidSimulate a real contact.
contactobjectOr a hypothetical one: { status?, source?, timezone?, tags?, attributes? }.
eventobject{ type, payload? } — the event that would have triggered it.
{
"ok": true,
"path": [{ "nodeId": "n0", "kind": "trigger" }, { "nodeId": "n1", "kind": "send_email", "detail": "welcome" }],
"sends": ["welcome", "getting-started"],
"endedBy": "exit"
}

endedBy: exit (clean finish), goal (goal condition met), dead_end (a branch leads nowhere — a bug in the graph), hop_guard (step ceiling hit, usually a loop).

404 — unknown key, or the graph has no entry node.

POST /api/v1/sequences/:key/enroll

Body: { "email": "ada@example.com" } or { "contactId": "9f0c…" } — one is required.

{ "ok": true, "enrollmentId": "e5f6…" }

422 — neither field given, or an enroll_failed reason (sequence not active, contact unsubscribed, already enrolled under once_* re-entry). 404 — unknown contact or sequence.

POST /api/v1/sequences/:key/enroll/bulk

Body: { "contactIds": [...] } or { "emails": [...] }.

{ "ok": true, "requested": 5000, "enrolled": 4812, "skipped": 188 }

skipped counts contacts already active in the sequence. The SDK chunks inputs above 5000 recipients into sequential requests and sums the totals; raw callers should do the same.