Campaigns and sequences

Multi-step sequences (campaigns), their settings and steps, enrolling leads, and how the runner executes each step.

A sequence (shown as a campaign in the app) is an ordered list of steps that ZeroReach runs for each enrolled lead.

Status

A sequence moves draft → active ⇄ paused, and any status can go to archived, which is final:

  • Activating needs at least one step, and a subject (or AI brief) on the first email step. A sequence with email steps also needs the workspace's postal address, for the unsubscribe footer (409 missing_postal_address).
  • Archiving stops every in-flight enrollment (stop_reason sequence_archived). Archived sequences are read-only.
  • Deleting only works for a draft that never had enrollments (admin+); otherwise archive it.

Starting a campaign is human-only

Activating or resuming a sequence starts sending, so Zero never does it. Over the API, POST /api/v1/sequences/{sequence_id}/activate needs the sequences:write scope.

Settings

SettingDefaultMeaning
timezoneUTCThe time zone of the send window.
send_window_start / send_window_end09:00 / 17:00When steps may run.
send_daysThe weekdays steps may run on.
daily_cap50Most steps run per day for this sequence (1 to 2000).
stop_on_replytrueStop a lead's enrollment as soon as they reply.

Changing the settings reschedules active enrollments.

Steps

Each step has a channel, a delay (delay_days, delay_hours) and its content:

ChannelWhat happens
emailAn email from a connected mailbox: a fixed subject and body (templates with lead variables, optional A/B variants), or an ai_brief that the Writer drafts per lead.
callA call by the voice agent, after the calling checks.
linkedin_taskA task for a person to do on LinkedIn.
manual_taskA task for a person.

You can always edit a step's content and delay; a delay change reschedules the enrollments waiting on that step. You can always append a step. Inserting in the middle, deleting or reordering steps is refused (409) while the sequence has in-flight enrollments.

Enrolling leads

POST /api/v1/sequences/{sequence_id}/enrollments enrolls leads in bulk; a saved list can be enrolled with POST /api/v1/lists/{list_id}/enroll. Enrolling is idempotent per lead. Leads that can't be enrolled come back in skipped with a reason:

  • not_found: missing, deleted or another workspace's lead;
  • already_enrolled;
  • missing_email, missing_phone or missing_linkedin_url, for the first step's channel;
  • contact_denied:<channel>:<reason>, when consent or the suppression list forbids marketing contact (for example contact_denied:email:suppressed:email:opt_out).

An enrollment is active, paused, completed, stopped or failed.

How the runner sends

A background runner executes due steps, safely, from any number of workers. For each due enrollment it:

  1. Checks the lead and the step:
    • stop on reply;
    • move the step into the send window;
    • run the compliance check for the step's channel. A call step denied only by the calling window or call caps is deferred to when it is allowed;
    • apply the sequence's daily cap;
    • check the postal-address gate.
  2. Prepares the step: tasks, AI drafts, mailbox rotation, the A/B variant and templates. It records the attempt with an idempotency key.
  3. Sends through the mailbox. Suppression and the contact check run again, with the mailbox's cap, a one-click unsubscribe link and the CAN-SPAM footer. On success the enrollment moves on to the next step.

The runner is crash-safe. If a worker dies after the provider accepted an email, the next run resends with the same idempotency key, and the provider returns the original message instead of sending twice.

AI-written steps

For a step with an ai_brief, the Writer drafts the email from the lead's research, the workspace profile and the knowledge base. Only an approved draft is ever sent. Whether a draft is approved automatically depends on the workspace's Email approval setting; see Approvals and autonomy.

If the AI budget is used up or no model is configured, the enrollment waits (ai_unavailable). It never loops and never falls back to a paid model.