Skip to content

Sequences API

Create and maintain simple lifecycle sequences through the server-side Integration API at /integration/v1/email/sequences. Each step uses either a published template or direct subject/body copy, with a delay in hours or days.

An empty funnelIds array means the sequence applies to all funnels in the project. Set it to one or more funnel IDs to scope the sequence.

Node.js

ts
import { createEmailsClient } from '@funnelsgrove/emails';

const emails = createEmailsClient({
  privateToken: process.env.FUNNELSGROVE_PRIVATE_TOKEN!,
});

const sequence = await emails.createSequence({
  slug: 'lead-follow-up',
  name: 'Lead follow-up',
  draft: {
    triggerEventType: 'email_captured',
    funnelIds: [], // all funnels; use ['funnel-uuid'] to scope it
    steps: [
      {
        key: 'welcome',
        mode: 'template',
        template: 'welcome',
        variables: { firstName: 'first_name' },
        delay: { value: 0, unit: 'hours' },
      },
      {
        key: 'reminder',
        mode: 'direct',
        subject: 'A quick reminder',
        body: 'Come back when you are ready.',
        delay: { value: 2, unit: 'days' },
      },
    ],
    exitEventTypes: ['purchase_completed'],
  },
});

await emails.addSequenceStep(sequence.id, {
  key: 'last-call',
  mode: 'direct',
  subject: 'Last call',
  body: 'This is your final reminder.',
  delay: { value: 48, unit: 'hours' },
});

await emails.validateSequence(sequence.id);
await emails.publishSequence(sequence.id);
await emails.enableSequence(sequence.id);

// Stops new enrollments without cancelling existing active enrollments.
await emails.disableSequence(sequence.id);

Template-mode steps must reference a published template. The sequence API resolves each template slug to the current published template version when the draft is created or updated. Its variables map binds each declared template variable to a lifecycle value.

Cancel an enrollment

Cancel the active enrollment for one sequence and funnel user when the user no longer needs the sequence:

ts
const result = await emails.cancelSequenceEnrollment(sequence.id, {
  funnelEndUserId: '00000000-0000-4000-8000-000000000000',
  reason: 'customer_request',
});

console.log(result.cancelled);

The operation is idempotent. The first request returns the number of active enrollments that were cancelled. Retrying after the enrollment is terminal succeeds with cancelled: 0.

The equivalent Integration API request is:

bash
curl --request POST \
  "https://sdk-api.funnelsgrove.com/integration/v1/email/sequences/$SEQUENCE_ID/enrollments/$FUNNEL_END_USER_ID/cancel" \
  --header "Authorization: Bearer $FUNNELSGROVE_PRIVATE_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{"reason":"customer_request"}'

Cancelling the enrollment prevents later lifecycle sends, including queued deliveries that have not passed the pre-send enrollment check. It cannot recall an email that the provider has already started sending or delivered.

Project CLI

bash
fgrove email pull --workspace "$WORKSPACE" --project "$PROJECT"

fgrove email sequence add-step lead-follow-up \
  --workspace "$WORKSPACE" \
  --project "$PROJECT" \
  --key reminder \
  --template reminder \
  --delay-days 2

fgrove email sequence add-step lead-follow-up \
  --workspace "$WORKSPACE" \
  --project "$PROJECT" \
  --key last-call \
  --subject "Last call" \
  --body "This is your final reminder." \
  --delay-hours 48

fgrove email validate
fgrove email push --workspace "$WORKSPACE" --project "$PROJECT"
fgrove email sequence publish lead-follow-up --workspace "$WORKSPACE" --project "$PROJECT"

Edit emails/sequences/lead-follow-up.json to replace the ordered step list or change its funnelIds; an empty list means all funnels.

To cancel one user enrollment through the same project CLI, keep the pushed sequence file locally and provide a project private token:

bash
export FUNNELSGROVE_PRIVATE_TOKEN="fg_private_..."

fgrove email sequence cancel lead-follow-up \
  --user-id "$FUNNEL_END_USER_ID" \
  --reason customer_request

The CLI verifies that the local sequence ID still belongs to the local slug before it sends the cancellation request.

Private sequence endpoints

All requests use Authorization: Bearer $FUNNELSGROVE_PRIVATE_TOKEN.

MethodEndpointNode.js SDK
GET/integration/v1/email/sequencesemails.listSequences()
GET/integration/v1/email/sequences/:sequenceIdemails.getSequence(sequenceId)
POST/integration/v1/email/sequencesemails.createSequence(input)
PATCH/integration/v1/email/sequences/:sequenceIdemails.updateSequence(sequenceId, input)
POST/integration/v1/email/sequences/:sequenceId/stepsemails.addSequenceStep(sequenceId, step)
POST/integration/v1/email/sequences/:sequenceId/validateemails.validateSequence(sequenceId)
POST/integration/v1/email/sequences/:sequenceId/publishemails.publishSequence(sequenceId)
POST/integration/v1/email/sequences/:sequenceId/enableemails.enableSequence(sequenceId)
POST/integration/v1/email/sequences/:sequenceId/disableemails.disableSequence(sequenceId)
POST/integration/v1/email/sequences/:sequenceId/enrollments/:funnelEndUserId/cancelemails.cancelSequenceEnrollment(sequenceId, input)