DocumentationEnrollment & triggers

Enrollment & triggers

Enroll contacts into an active sequence, the enrollment lifecycle and the real state of triggers.

A contact enters a sequence through an enrollment. Each enrollment carries its own position and status, independent of the rest.

Enroll a contact

POST /sequences/{id}/enroll
Authorization: Bearer <apiKey or accessToken>
X-Tenant-Id: {tenantId}
Content-Type: application/json

{
  "contactId": "cont_123"
}

Requires the sequences:write scope. For the enrollment to succeed:

  • The sequence must be ACTIVE.
  • The contact must belong to your tenant.
  • It must be opted in and not on the suppression list.
  • It cannot already be enrolled in that sequence (one enrollment per contact and sequence).

Enrollment lifecycle

StatusMeaning
ACTIVEMoving through the steps
COMPLETEDReached the end of the sequence
CANCELLEDStopped: the contact unsubscribed or was suppressed, or the recipient was rejected
PAUSEDThe sequence was set to PAUSED
FAILEDThe send exhausted its retries, or the template/sequence is missing

Exits happen on their own: if the contact is suppressed or unsubscribes mid-flow, its enrollment moves to CANCELLED and receives no further steps. Pausing the sequence moves in-flight enrollments to PAUSED.

List enrollments

GET /sequences/{id}/enrollments?page=1&limit=20&status=ACTIVE

Requires sequences:read. Returns paginated enrollments; filter by status with any of the values in the table above.

Automatic triggers

When you create the sequence you set a triggerType and its triggerConfig. Beyond manual enrollment, all three automatic triggers now enroll contacts on their own.

triggerTypetriggerConfigWhen it enrolls
MANUALOnly via POST /sequences/:id/enroll
CONTACT_CREATED{ "requireOptIn": true }When an opted-in contact is created
CONTACT_TAGGED{ "tag": "vip" }When that tag is added to a contact
API_EVENT{ "eventName": "signup" }When that event is received via the API

CONTACT_CREATED

Creating an opted-in contact (via PUT /contacts or the SDK) enrolls it into every ACTIVE sequence with this trigger. The fan-out is asynchronous (the sequence-trigger queue), so a bulk import does not slow the request.

CONTACT_TAGGED

Tagging a contact with the configured tag enrolls it into the ACTIVE sequences waiting for it:

POST /contacts/{idOrExternalId}/tags
Authorization: Bearer <apiKey or accessToken>
X-Tenant-Id: {tenantId}
Content-Type: application/json

{
  "tags": ["vip"]
}

Requires contacts:write. It only fires the first time the tag is added (idempotent: re-tagging does not re-enroll). Removing the tag with DELETE /contacts/{idOrExternalId}/tags/{tag} does not cancel an enrollment already created. Enrollment is asynchronous too.

API_EVENT

Fire your own event; the sequences whose triggerConfig.eventName matches enroll the contact:

POST /sequences/events
Authorization: Bearer <apiKey or accessToken>
X-Tenant-Id: {tenantId}
Content-Type: application/json

{
  "eventName": "signup",
  "contactId": "cont_123"
}

Requires sequences:write. It responds { "matched": N, "enrolled": M }: how many sequences matched and how many actually enrolled (already-enrolled or non-opted-in contacts are skipped). Unlike the other two, it is synchronous.