Sequence endpoints

Business rules are in Nurture sequences; schemas in lib/schema/sequences.ts.

Sequence endpoints use page_size, not pageSize.

Templates

GET /api/sequence-templates

page, page_size.

Filters

search, type (onboarding, re_engagement, not_connected_outreach), is_active

Response

{ data, total, page, page_size }, each template including its full steps array.

POST /api/sequence-templates

Validated by CreateSequenceTemplateSchema.

{
  "name": "Onboarding — 5 touch",
  "type": "onboarding",
  "goal": "Book a demo",
  "description": "…",
  "call_campaign_default": false,
  "email_campaign_default": true,
  "sms_campaign_default": false,
  "is_active": true,
  "steps": [
    { "channel": "email", "delay_hours": 0, "name": "Intro", "email_tone": "warm", "is_ai_generated": true },
    { "channel": "wait",  "delay_hours": 48, "delay_unit": "hours" },
    { "channel": "data_gate",
      "delay_hours": 0,
      "gate_bigquery_view": "outreach_app.contacts",
      "gate_match_mode": "all",
      "gate_conditions": [{ "field": "deal_id", "operator": "is_not_null", "value": null }],
      "true_branch_step_index": 5,
      "gate_retry_delay_hours": 24,
      "gate_max_retries": 3 },
    { "channel": "crm_event",
      "delay_hours": 0,
      "extra_configs": {
        "event_type": "Contact.Update",
        "body": { "hubspot_contact_id": "{{hubspot_contact_id}}", "last_touch": "{{now}}" }
      } }
  ]
}

Validation rules worth knowing:

  • gate_conditions operators are is_not_null, is_null, equals, not_equals, gt, lt, is_in, is_not_in. value may be null only for the null checks; is_in / is_not_in take a comma-separated list.

  • A crm_event step’s extra_configs must satisfy CrmEventConfigSchema. event_type must be one of the registry values, and the reserved key event_type inside body is rejected.

  • delay_unit is hours or minutes and is only meaningful for wait.

GET /api/sequence-templates/{id}

Template with steps.

PATCH /api/sequence-templates/{id}

Validated by UpdateSequenceTemplateSchema. Supplying steps replaces the step set. Existing ContactSequenceStep logs survive, because templateStepId is onDelete: SetNull — the link is severed, the history kept.

DELETE /api/sequence-templates/{id}

POST /api/sequence-templates/{id}/duplicate

Deep-copies the template and its steps.

Enrollments

GET /api/contact-sequences

page, page_size.

Filters

search, status (pending, active, paused, completed, abandoned, exited)

Each row includes contact_name, contact_email, contact_company, template_name, template_type, current_step_index, total_steps, next_step_at and the lifecycle timestamps.

POST /api/contact-sequences

Validated by EnrollContactSequenceSchema.

{
  "contact_id": "uuid",
  "template_id": "uuid",
  "campaign_id": null,
  "campaign_target_id": null,
  "email_campaign_target_id": null
}

SequenceExecutionService.enrollContact enforces one active sequence per contact in application code — a database unique constraint would wrongly block multiple completed or paused enrollments.

GET /api/contact-sequences/{id}

Full detail: the enrollment, its execution_id (the Inngest run id), the template with steps, and the ContactSequenceStep timeline. Each log entry carries status, outcome, gate_retry_count, executed_at, the artifact ids (call_id, email_id, follow_up_task_id, activity_id) and the raw response payload.

POST /api/contact-sequences/{id}/start

Sets the enrollment active and sends sequence/execute, which claims the run by stamping executionId.

POST /api/contact-sequences/{id}/stop

Sends sequence/cancel. The sequenceExecute function declares cancelOn: [{ event: "sequence/cancel", match: "data.contactSequenceId" }], so the run is torn down immediately — including an in-progress step.sleep.

POST /api/contact-sequences/{id}/restart

Resets progress and starts a new run. A terminal sequence is refused by startExecution unless it has been restarted through this endpoint.