Sequences
Create, manage, and monitor automated email sequences with steps, delays, and conditions.
Sequences are automated drip campaigns that send a series of emails to contacts over time. Each sequence is defined as a YAML document with steps, delays, and conditions. Contacts are enrolled in sequences manually or via event-based triggers.
Create or update sequence
PUT /api/sequences/:idCreates a new sequence or updates an existing one. The sequence body is a YAML document. You can send it two ways:
- Raw YAML — set
Content-Type: text/yaml(or omit JSON) and put the YAML in the request body. - JSON wrapper — set
Content-Type: application/jsonand send{ "content": "<yaml>", "name"?, "enabled"? }.
The sequence's name and trigger come from the YAML (top-level name and trigger.event), unless overridden by the JSON name. Every template referenced by the YAML steps must already exist, or the request fails with TEMPLATE_NOT_FOUND. Per the engine contract, a PUT with new content never re-enables a disabled sequence — use PATCH { "enabled": true } for that.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | A unique identifier for the sequence (e.g. onboarding, re-engagement). |
Request body (JSON form)
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes | The YAML sequence definition. Must include a trigger.event and (for create) a name. |
name | string | No | Overrides the name from the YAML. |
enabled | boolean | No | Sets the enabled state on create. |
Response
Returns the sequence metadata (201 on create, 200 on update). The YAML content itself is not echoed back; fetch it with GET /api/sequences/:id.
{
"sequenceId": "onboarding",
"name": "Onboarding Sequence",
"version": 4,
"enabled": true,
"triggerEvent": "signed_up",
"createdAt": "2025-09-10T08:00:00.000Z",
"updatedAt": "2025-09-15T14:00:00.000Z"
}If the YAML validates with warnings, a warnings array is also included.
Errors
| Code | Description |
|---|---|
VALIDATION_ERROR | Invalid YAML, missing sequence name, or missing trigger event. |
TEMPLATE_NOT_FOUND | The YAML references a template that does not exist. |
PLAN_LIMIT_EXCEEDED | The plan's sequence limit has been reached. |
Examples
curl -X PUT https://api.kraiter.com/api/sequences/onboarding \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "name: Onboarding Sequence\ntrigger:\n event: signed_up\nsteps:\n - send: welcome-email\n - delay: 1d\n - send: getting-started\n - delay: 3d\n - send: tips-and-tricks\n"
}'const sequence = await kraiter.sequences.put("onboarding", {
content: `name: Onboarding Sequence
trigger:
event: signed_up
steps:
- send: welcome-email
- delay: 1d
- send: getting-started
- delay: 3d
- send: tips-and-tricks`,
});Partial update sequence
PATCH /api/sequences/:idUpdates a sequence's metadata without replacing the YAML definition. This is how you enable or disable a sequence. At least one field must be provided.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The sequence ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | New name for the sequence. |
enabled | boolean | No | Enable (true) or disable (false) the sequence. |
triggerEvent | string | No | Change the event that enrols contacts into the sequence. |
A sequence does not have a draft/active/paused status. Its running state is the boolean enabled field: a disabled sequence (enabled: false) enrols no new contacts and executes no steps. Enabled flags never flip implicitly — only an explicit PATCH { "enabled": ... } changes them.
Examples
curl -X PATCH https://api.kraiter.com/api/sequences/onboarding \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "enabled": true }'const sequence = await kraiter.sequences.update("onboarding", {
enabled: true,
});List sequences
GET /api/sequencesReturns a paginated list of sequences.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
cursor | string | — | Pagination cursor. |
limit | number | 20 | Number of sequences to return (max 100). |
The response is an object with an items array and a nextCursor.
Examples
curl "https://api.kraiter.com/api/sequences?limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"const sequences = await kraiter.sequences.list({ limit: 20 });Get sequence
GET /api/sequences/:idReturns a single sequence including its full YAML definition and triggers.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The sequence ID. |
Errors
| Code | Description |
|---|---|
NOT_FOUND | No sequence with this ID exists. |
Examples
curl https://api.kraiter.com/api/sequences/onboarding \
-H "Authorization: Bearer YOUR_API_KEY"const sequence = await kraiter.sequences.get("onboarding");Delete sequence
DELETE /api/sequences/:idPermanently deletes a sequence. Active enrolments are cancelled and pending steps are removed.
Response
Returns 204 No Content on success.
Errors
| Code | Description |
|---|---|
NOT_FOUND | No sequence with this ID exists. |
Examples
curl -X DELETE https://api.kraiter.com/api/sequences/onboarding \
-H "Authorization: Bearer YOUR_API_KEY"await kraiter.sequences.delete("onboarding");Get sequence run status
GET /api/sequences/:id/statusReturns the sequence metadata plus a breakdown of enrolled contacts by state.
Response
{
"sequenceId": "onboarding",
"name": "Onboarding Sequence",
"enabled": true,
"version": "01H8...",
"createdAt": "2025-09-10T08:00:00.000Z",
"updatedAt": "2025-09-15T14:00:00.000Z",
"contacts": {
"active": 340,
"paused": 0,
"completed": 870,
"exited": 40
}
}Examples
curl https://api.kraiter.com/api/sequences/onboarding/status \
-H "Authorization: Bearer YOUR_API_KEY"const status = await kraiter.sequences.getStatus("onboarding");List enrolled contacts
GET /api/sequences/:id/contactsReturns contacts currently enrolled in the sequence.
Examples
curl "https://api.kraiter.com/api/sequences/onboarding/contacts?limit=20" \
-H "Authorization: Bearer YOUR_API_KEY"const contacts = await kraiter.sequences.listContacts("onboarding");List sends from sequence
GET /api/sequences/:id/sendsReturns all emails sent as part of this sequence.
Examples
curl "https://api.kraiter.com/api/sequences/onboarding/sends" \
-H "Authorization: Bearer YOUR_API_KEY"const sends = await kraiter.sequences.listSends("onboarding");Dry-run sequence
POST /api/sequences/:id/dry-runEvaluates all gates, step conditions, and exit conditions for a specific contact without persisting anything or sending. Returns a step-by-step decision path explaining the outcome.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
contactId | string | Yes | The contact to simulate the sequence for. |
stepId | string | No | Evaluate a specific step. Defaults to the contact's current step (or the first step). |
Response
{
"outcome": "would_send",
"decisionPath": [
{ "stage": "gates", "passed": true, "explanation": "All gates passed", "details": {} },
{ "stage": "step_conditions", "passed": true, "explanation": "Step conditions met", "details": {} }
],
"stepInfo": { "stepId": "welcome-email", "templateId": "welcome-email" },
"templatePreview": { "subject": "Welcome, Alice!", "textPreview": "Hello Alice..." },
"contactInfo": {
"contactId": "cnt_01H8MZXK...",
"email": "alice@example.com",
"subscribed": true,
"suppressed": false
},
"sequenceInfo": {
"sequenceId": "onboarding",
"name": "Onboarding Sequence",
"enabled": true,
"version": "01H8..."
},
"inSequence": false,
"timestamp": "2025-09-15T14:00:00.000Z"
}outcome is one of would_send, would_skip, would_exit, or blocked. templatePreview is present only when the outcome is would_send.
Examples
curl -X POST https://api.kraiter.com/api/sequences/onboarding/dry-run \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "contactId": "cnt_01H8MZXK..." }'const dryRun = await kraiter.sequences.dryRun("onboarding", {
contactId: "cnt_01H8MZXK...",
});