Sequences API
The Sequences API manages email sequences (multi-step drip / outbound automations) and the enrollments that drive contacts through them. Adding a contact and enrolling it is fully API-driven: once a contact is enrolled, the sequence processor sends each step automatically on schedule — no dashboard interaction required.
REST API. These are REST v1 endpoints authenticated with an API key, not tRPC. Send
Authorization: Bearer rk_live_…and a JSON body. Successful responses are wrapped in{ "data": … }(lists add a"meta"block). Generate a key under Settings → API Keys.
Required Scopes
| Scope | Grants |
|---|---|
sequences:read | List/view sequences and enrollments |
sequences:write | Create/update/delete sequences; enroll, pause, resume, and unenroll contacts |
sequences:write implies sequences:read.
Sequence Object
{
id: string; // UUID
organization_id: string; // UUID
name: string;
description?: string | null;
trigger_type: 'manual' | 'webhook' | 'form_submission' | 'tag_added' | 'contact_created';
trigger_config: object; // e.g. { webhook_id, tag, list_id }
exit_conditions: object; // e.g. { replied: true, unsubscribed: true }
status: 'draft' | 'active' | 'paused' | 'archived';
from_email?: string | null;
from_name?: string | null;
reply_to_email?: string | null;
total_enrolled: number;
total_completed: number;
total_unsubscribed: number;
created_at: string; // ISO timestamp
steps?: SequenceStep[]; // included on GET /sequences/:id
}
List Sequences
GET /api/v1/sequences
Scope: sequences:read
Query params: status (draft|active|paused|archived), limit (1-100, default 50), offset (default 0).
curl "https://crm.switchlabs.dev/api/v1/sequences?status=active&limit=20" \
-H "Authorization: Bearer rk_live_xxx"
Response:
{
"data": [ { "id": "uuid", "name": "Press Outreach", "status": "active", "total_enrolled": 42 } ],
"meta": { "total": 3, "limit": 20, "offset": 0, "next_offset": null }
}
Create Sequence
POST /api/v1/sequences
Scope: sequences:write
Sequences are created as a draft. Add steps in the dashboard (or set them up server-side), then activate with PATCH … { "status": "active" }.
Body:
{
name: string; // required, 1-200 chars
description?: string;
trigger_type?: 'manual' | 'webhook' | 'form_submission' | 'tag_added' | 'contact_created'; // default 'manual'
trigger_config?: object;
exit_conditions?: { replied?: boolean; converted?: boolean; unsubscribed?: boolean; manual?: boolean; bounced?: boolean };
from_email?: string;
from_name?: string;
reply_to_email?: string;
}
curl -X POST "https://crm.switchlabs.dev/api/v1/sequences" \
-H "Authorization: Bearer rk_live_xxx" -H "Content-Type: application/json" \
-d '{ "name": "Press Outreach", "trigger_type": "manual" }'
Returns 201 with the created sequence.
Get / Update / Delete Sequence
GET /api/v1/sequences/:id — returns the sequence with its steps (ordered by position). Scope: sequences:read.
PATCH /api/v1/sequences/:id — update any of name, description, trigger_type, trigger_config, exit_conditions, from_email, from_name, reply_to_email, status. Scope: sequences:write.
Setting
statustoactiverequires the sequence to have at least one step (returns400otherwise) — the same rule as the dashboard.
DELETE /api/v1/sequences/:id — delete the sequence (cascades steps and enrollments). Returns 204. Scope: sequences:write.
curl -X PATCH "https://crm.switchlabs.dev/api/v1/sequences/SEQ_ID" \
-H "Authorization: Bearer rk_live_xxx" -H "Content-Type: application/json" \
-d '{ "status": "active" }'
Enroll a Contact
POST /api/v1/sequences/:id/enroll
Scope: sequences:write
Enroll one contact, identified by customer_id or email (resolved to an existing contact in your org). Applies the same eligibility guardrails as the dashboard "Enroll" action.
Body:
{
customer_id?: string; // UUID — provide this OR email
email?: string; // resolved to an existing contact
enrollment_source?: string; // default "api"
enrollment_data?: object; // arbitrary metadata stored on the enrollment
}
curl -X POST "https://crm.switchlabs.dev/api/v1/sequences/SEQ_ID/enroll" \
-H "Authorization: Bearer rk_live_xxx" -H "Content-Type: application/json" \
-d '{ "email": "reporter@example.com", "enrollment_source": "press_list" }'
Responses:
| Status | Meaning |
|---|---|
201 | Enrolled — body is the enrollment object |
404 | Sequence or contact not found in your org |
409 | Contact already enrolled in this sequence |
422 | Contact ineligible for outreach — body error.details.reason explains why (contact_status:unsubscribed, converted_customer, active_ticket:open, …) |
Bulk Enroll
POST /api/v1/sequences/:id/enroll_bulk
Scope: sequences:write
Enroll up to 1000 contacts in one call, by customer_ids and/or emails. Eligibility is applied per contact; ineligible / already-enrolled / unmatched contacts are skipped (not errors).
Body:
{
customer_ids?: string[]; // UUIDs
emails?: string[]; // resolved to existing contacts
enrollment_source?: string; // default "api_bulk"
}
curl -X POST "https://crm.switchlabs.dev/api/v1/sequences/SEQ_ID/enroll_bulk" \
-H "Authorization: Bearer rk_live_xxx" -H "Content-Type: application/json" \
-d '{ "emails": ["a@example.com", "b@example.com"] }'
Response:
{
"data": {
"enrolled": 1,
"skipped": 1,
"results": [
{ "customerId": "uuid", "status": "enrolled" },
{ "email": "b@example.com", "status": "skipped", "code": "customer_not_found", "reason": "No contact found with this email" }
]
}
}
List Enrollments
GET /api/v1/sequences/:id/enrollments
Scope: sequences:read
Query params: status (active|paused|completed|exited|failed), limit, offset. Each row embeds a customer summary.
Pause / Resume / Unenroll
PATCH /api/v1/sequences/:id/enrollments/:enrollmentId — set { "status": "paused" } to pause or { "status": "active" } to resume. Scope: sequences:write.
DELETE /api/v1/sequences/:id/enrollments/:enrollmentId — unenroll (exit). Optional ?reason= (replied|converted|unsubscribed|manual|bounced, default manual). Cancels any pending drafts for the enrollment. Scope: sequences:write.
curl -X DELETE "https://crm.switchlabs.dev/api/v1/sequences/SEQ_ID/enrollments/ENR_ID?reason=replied" \
-H "Authorization: Bearer rk_live_xxx"
Auto-Enrollment via the Customers API
Creating a contact through POST /api/v1/customers automatically enrolls it into any active sequence whose trigger_type is contact_created, and (when tags are supplied) any tag_added-triggered sequence. This is the lowest-friction way to "add a lead and start outreach" in a single call — no explicit enroll needed.
How Sending Works
Enrollment only schedules the first step (next_send_at). The sequence-processor cron sends each step on schedule and advances the enrollment automatically. You never call a "send" endpoint for sequences — enroll and the engine takes over.
See also: Campaigns · Marketing Lists · Customers