Appearance
Email sequences
A sequence sends a series of templates after a trigger event. It has a draft you edit, published versions, and an isActive switch. Enrollment happens only while the sequence is enabled and has a published version. The Node.js client @funnelsgrove/emails and the fgrove email sequence CLI call these endpoints; see Send lifecycle emails for the workflow.
Every endpoint below takes projectId as a query parameter with an API key.
json
{
"id": "<sequence-id>",
"slug": "lead-follow-up",
"name": "Lead follow-up",
"currentPublishedVersionId": null,
"isActive": false,
"draft": {
"triggerEventType": "email_captured",
"funnelIds": [],
"steps": [{
"key": "welcome",
"mode": "template",
"templateVersionId": "<template-version-id>",
"variables": { "firstName": "first_name" },
"personalizationEnabled": false,
"delaySeconds": 0
}],
"exitEventTypes": ["purchase_completed"],
"lifecycleConsentRequired": true,
"unsubscribeEnabled": true
}
}Draft fields
| Field | Required | Purpose |
|---|---|---|
triggerEventType | Yes | email_captured, purchase_completed, registration_completed or payment_upcoming. |
funnelIds | No | Up to 50 funnel UUIDs. Empty (the default) means every funnel in the project. |
steps | Yes | 1 to 50 steps, in sending order. |
exitEventTypes | No | Events that stop the sequence for a user: purchase_completed and/or registration_completed. Default ["purchase_completed"]. |
lifecycleConsentRequired | No | Require a current lifecycle marketing opt-in. Default true. Set false only when you have another valid basis for the messages; it does not bypass unsubscribe, bounce, complaint or manual suppression. |
unsubscribeEnabled | No | Keep the unsubscribe link. Default true. |
Step fields
| Field | Required | Purpose |
|---|---|---|
key | Yes | Lowercase letters, digits and hyphens, up to 64 characters; unique in the sequence. |
delay | Yes | { "value": <0..2160>, "unit": "hours" | "days" }. The first step waits from the trigger, later steps from the preceding send. Maximum 90 days. |
mode | No | template (default) or direct. |
template | Template mode | Slug of a published template. The draft pins the current published version when it is created or updated. |
variables | Template mode | Binds each declared template variable to a lifecycle value. |
personalizationEnabled | Template mode | true requests an email.personalization_requested webhook 20 minutes before the step sends. Default false. |
subject, body | Direct mode | Authored copy for a direct step. |
Direct steps
Direct-copy steps are accepted by the API but cannot yet persist a lifecycle delivery. Do not publish direct steps until that implementation defect is fixed.
payment_upcoming sequences follow extra rules: exactly one template step, a delay (lead time before the charge) from 1 hour to 45 days, no personalization and no exit events. See Upcoming renewal reminder.
Published versions also carry the resolved sender (domainId, domain, fromLocalPart, displayName).
List sequences
Scope: email_sequences:read
Request
http
GET /email/sequencesExample
sh
curl "https://sdk-api.funnelsgrove.com/integration/v1/email/sequences" \
-H "Authorization: Bearer <project-sdk-secret-key>"Response
A JSON array of sequences.
Get sequence
Scope: email_sequences:read
Request
http
GET /email/sequences/:sequenceIdExample
sh
curl "https://sdk-api.funnelsgrove.com/integration/v1/email/sequences/<sequence-id>" \
-H "Authorization: Bearer <project-sdk-secret-key>"Response
json
{
"sequence": { "id": "<sequence-id>", "slug": "lead-follow-up", "…": "…" },
"versions": [{ "id": "<version-id>", "sequenceId": "<sequence-id>", "version": 1, "sender": { "…": "…" }, "publishedAt": "…", "…": "…" }]
}Errors
| Status | Meaning |
|---|---|
400 | sequenceId is not a UUID. |
404 | The sequence does not exist in this project. |
Create sequence
Scope: email_sequences:write
Request
http
POST /email/sequences
Content-Type: application/jsonParameters
| Field | Required | Purpose |
|---|---|---|
slug | Yes | Lowercase letters, digits and hyphens, up to 80 characters. |
name | Yes | Display name, up to 120 characters. |
draft | Yes | The draft fields. |
Example
sh
curl -X POST "https://sdk-api.funnelsgrove.com/integration/v1/email/sequences" \
-H "Authorization: Bearer <project-sdk-secret-key>" \
-H "Content-Type: application/json" \
--data '{
"slug": "lead-follow-up",
"name": "Lead follow-up",
"draft": {
"triggerEventType": "email_captured",
"funnelIds": [],
"steps": [
{ "key": "welcome", "template": "welcome", "variables": { "firstName": "first_name" }, "delay": { "value": 0, "unit": "hours" } },
{ "key": "reminder", "template": "reminder", "variables": { "firstName": "first_name" }, "delay": { "value": 2, "unit": "days" } }
],
"exitEventTypes": ["purchase_completed"]
}
}'Response
201 with the sequence.
Errors
| Status | Meaning |
|---|---|
400 | The body is invalid, or a step references a template that is not published. |
409 | A sequence with this slug already exists. |
Update sequence
Replaces the whole draft.
Scope: email_sequences:write
Request
http
PATCH /email/sequences/:sequenceId
Content-Type: application/jsonParameters
| Field | Required | Purpose |
|---|---|---|
name | No | New display name. |
draft | Yes | The complete draft. |
Example
sh
curl -X PATCH "https://sdk-api.funnelsgrove.com/integration/v1/email/sequences/<sequence-id>" \
-H "Authorization: Bearer <project-sdk-secret-key>" \
-H "Content-Type: application/json" \
--data '{ "draft": { "triggerEventType": "email_captured", "steps": [ { "key": "welcome", "template": "welcome", "delay": { "value": 1, "unit": "hours" } } ] } }'Response
The updated sequence.
Errors
| Status | Meaning |
|---|---|
400 | The draft is invalid or references an unpublished template. |
404 | The sequence does not exist in this project. |
Add step
Appends one step to the draft.
Scope: email_sequences:write
Request
http
POST /email/sequences/:sequenceId/steps
Content-Type: application/jsonParameters
The step fields.
Example
sh
curl -X POST "https://sdk-api.funnelsgrove.com/integration/v1/email/sequences/<sequence-id>/steps" \
-H "Authorization: Bearer <project-sdk-secret-key>" \
-H "Content-Type: application/json" \
--data '{ "key": "last-call", "template": "last-call", "variables": { "firstName": "first_name" }, "delay": { "value": 48, "unit": "hours" } }'Response
The updated sequence.
Errors
| Status | Meaning |
|---|---|
400 | The step is invalid or references an unpublished template. |
404 | The sequence does not exist in this project. |
Validate sequence
Checks that every referenced template is still published.
Scope: email_sequences:read
Request
http
POST /email/sequences/:sequenceId/validateExample
sh
curl -X POST "https://sdk-api.funnelsgrove.com/integration/v1/email/sequences/<sequence-id>/validate" \
-H "Authorization: Bearer <project-sdk-secret-key>"Response
json
{ "valid": true }Errors
| Status | Meaning |
|---|---|
400 | The draft is invalid. |
404 | The sequence does not exist in this project. |
Publish sequence
Publishes the draft as a new version. Enrollments created afterwards use it.
Scope: email_sequences:write
Request
http
POST /email/sequences/:sequenceId/publishExample
sh
curl -X POST "https://sdk-api.funnelsgrove.com/integration/v1/email/sequences/<sequence-id>/publish" \
-H "Authorization: Bearer <project-sdk-secret-key>"Response
json
{
"sequence": { "id": "<sequence-id>", "currentPublishedVersionId": "<version-id>", "…": "…" },
"version": { "id": "<version-id>", "version": 1, "sender": { "…": "…" }, "publishedAt": "…", "…": "…" }
}Errors
| Status | Meaning |
|---|---|
400 | The draft is invalid. |
404 | The sequence does not exist in this project. |
409 | The draft changed while publishing; read it again and retry. |
Enable or disable sequence
Enabling starts new enrollments; it requires a published version and a sending domain with a valid DMARC policy. Disabling stops new enrollments, and queued steps fail their pre-send check.
Scope: email_sequences:write
Request
http
POST /email/sequences/:sequenceId/enable
POST /email/sequences/:sequenceId/disableExample
sh
curl -X POST "https://sdk-api.funnelsgrove.com/integration/v1/email/sequences/<sequence-id>/enable" \
-H "Authorization: Bearer <project-sdk-secret-key>"Response
The sequence with its new isActive value.
Errors
| Status | Meaning |
|---|---|
400 | The sequence cannot be enabled; the message names the problem. |
404 | The sequence does not exist in this project. |
409 | The sequence has no published version. |
Cancel an enrollment
Cancels the active enrollment of one funnel user in one sequence. Idempotent: the first call returns how many active enrollments were cancelled; a retry after the enrollment is terminal returns cancelled: 0.
Scope: email_sequences:write
Request
http
POST /email/sequences/:sequenceId/enrollments/:funnelEndUserId/cancel
Content-Type: application/jsonParameters
| Parameter | In | Required | Purpose |
|---|---|---|---|
funnelEndUserId | Path | Yes | The funnel user's user.id from the funnel user bundle. |
reason | Body | No | Up to 120 characters, for your records. Default manual. |
Example
sh
curl -X POST "https://sdk-api.funnelsgrove.com/integration/v1/email/sequences/<sequence-id>/enrollments/<funnel-end-user-id>/cancel" \
-H "Authorization: Bearer <project-sdk-secret-key>" \
-H "Content-Type: application/json" \
--data '{ "reason": "customer_request" }'Response
json
{ "sequenceId": "<sequence-id>", "funnelEndUserId": "<funnel-end-user-id>", "cancelled": 1 }Cancelling prevents later lifecycle sends, including queued deliveries that have not passed their pre-send check. It cannot recall an email the provider has already started sending.
Errors
| Status | Meaning |
|---|---|
400 | An ID is not a UUID or reason is invalid. |
404 | The sequence does not exist in this project. |