Skip to main content
The Sequencer is in early access, so check the API changelog for changes that can affect an integration. This walks the whole loop: create a sequence, give it a template and steps, preview who would go in, enroll them, turn it on, then read and resolve the tasks it produces. Every call uses the x-api-key header and the base URL https://client-api.salesfinity.co.
/v2/sequencer/* is the Salesfinity Sequencer. The older GET /v1/sequences is a different thing entirely — it lists sequence names seen on your call logs from an external CRM. It is unchanged and unrelated.

Before you start: who is acting

An API key identifies a team, not a person. The Sequencer records a person on everything it writes — a sequence has an author, an enrollment has an owner, a task has an assignee — so every write takes a user_id, which must be an active member of the key’s team. Get member IDs from GET /v1/team. Cancelling an enrollment run is the one write where user_id is optional. Some endpoints also take owner_id or assignee_id. The distinction matters: owner_id and assignee_id default to user_id when omitted. On GET /v2/sequencer/tasks, user_id is instead a filter for the assignee, and it defaults to the whole team. Most lists return data and pagination (total, page, limit, pages); page with page and limit, up to 100 per page. A few short lists — a sequence’s steps, policies, enrollment runs and a task’s events — are not paged; their reference pages show the shape.

1. Create the sequence

The response is the sequence; keep its _id. It starts disabled and has no steps, and a sequence with no steps cannot be enrolled into. schedule is optional; without it the team’s default sending window applies. When you send one, send all five fields: timezone_source (prospect or account), an IANA timezone, days_of_week (one to seven distinct days, 0 is Sunday), and start_hour and end_hour (0–23, with end_hour later than start_hour). A partial schedule is refused with 400.

2. Create an email template

Automatic email steps send a template, so create one first:
Merge fields are {{first_name}}, {{last_name}}, {{company}}, {{title}} and {{custom.<label>}} for a custom field, matched by label. Check the result with POST /v2/sequencer/templates/{id}/preview, against a real contact or a sample one.

3. Add steps

Steps run in the order you create them, up to 50. interval_seconds is the wait after the previous step finished (an email sent, or a task completed or skipped) — or after enrollment, for the first one — and is required on a sequence timed by interval (the default).
auto_email sends on its own and needs at least one template. call, task and manual_email create a task and wait for the rep. Whatever the interval, a step only runs inside the sending window.
Reordering or deleting a step is refused with 409 while any enrollment is active or paused — renumbering would desync the step each prospect is sitting on. Disabling the sequence does not help, because disabling pauses its enrollments. To restructure a sequence that has people in it, duplicate it, edit the copy, and move the people with POST /v2/sequencer/enrollments/bulk/move. Appending a step and editing one in place are always allowed.

4. Preview, then enroll what you reviewed

Preview is free, changes nothing, and tells you who would actually go in:
The response has a preview_id, counts (total, will_add, needs_approval, excluded), excluded_reasons, and up to 200 sample rows, with the ones that need explaining first. Each row has a key such as email:[email protected], a status and a reason. Anything Salesfinity can answer from data it already holds is answered exactly; a rule that would need a live CRM read is listed under unmeasured rather than guessed. The source can be contacts (existing contact IDs), contact_list (a Salesfinity list), or rows (prospects you supply, such as a parsed CSV). To enroll exactly the set you reviewed, start a run with the preview as its source within 30 minutes; after that it is refused with 400 and you preview again:
A run takes up to 5,000 people and returns at once with a run_id. Poll GET /v2/sequencer/enrollment-runs/{run_id} until status is completed, failed or cancelled; it reports total, processed, enrolled, skipped_count, held_count and up to 50 sample_skipped. Rules are checked again as each person is added, so the outcome can differ from the preview if a record changed in between. To add someone a rule refused, pass their preview row key in force_include. They go in held for approval, not straight in. People on the do-not-contact list or already in the sequence cannot be forced. Because the sequence is still disabled, everyone enrolled now waits in pending. Step 6 starts them.

5. Or add up to 500 people in one request

When you already know who should go in, add them synchronously:
Identify each prospect by an existing contact ID, an email or a linkedin_username; an unknown email or handle is created as a contact on the way in. A 200 does not mean everyone went in. The response reports the whole outcome. Here it is with sequence_states, the enrollments it created, left out:
Read skipped on every call. prospect is the email, LinkedIn handle or contact ID you sent, and reason is a sentence. When a rule refused the person, the entry also has a rule object with the rule’s id, name, action and reason. held lists people a rule is holding for approval, and warnings lists rules that fired without blocking. A person can hold only one live enrollment per sequence, so sending the same person again is reported in skipped rather than creating a duplicate. For the machine-readable reason, read the refusal log in step 9. A request that applies to every row — the sequence is archived, has no steps, or has enrollment paused — fails as a whole with 422.

Retrying safely

Send an Idempotency-Key on every POST. Every POST under /v2/sequencer/ accepts one except the three /preview endpoints, which change nothing; PATCH, PUT and DELETE do not take one. A repeat with the same key returns the original response instead of acting twice. A repeat with the same key but a different method, path or body is a 409, because that means something is wrong on your side. A repeat that arrives while the first request is still running is also a 409; retry it after a moment. A request that fails frees its key, and keys are kept for 24 hours per team. See Retrying writes safely.

6. Turn it on

The response holds the sequence and activated, how many parked enrollments started moving. Enabling is refused with 422 when the sequence has no steps, is archived, has a sending window that never opens, or has an email step and no mailbox in its sending pool that can send. The sending pool is the sequence’s own email_account_ids if set; otherwise the mailboxes assigned to the sequence’s author (the user_id that created it); otherwise the team’s shared mailboxes. In GET /v2/sequencer/email-accounts a mailbox can send when is_connected is true, is_paused is false and can_send is true; assigned_to says whose it is.

7. Work the task queue

Without state, the list includes completed, skipped, expired and cancelled tasks too; pending is the working queue. Sorting and filtering are applied across the whole result set, not just the page you asked for. Resolve a task:
Completing or skipping a task advances the cadence — skipping still moves the contact on. Snoozing moves the due date, up to 365 days ahead, and moves the auto-skip deadline by the same amount.
  • Call tasks. Pass the call_log the task was resolved by; it must belong to your team. If the call was dialed from the rep’s Sequencer dial list in Salesfinity you do not need to call this at all: dispositioning the call completes the task and applies the disposition’s outcome, such as finishing the enrollment on meeting set. Completing over the API advances the cadence but applies no disposition outcome.
  • manual_email tasks. Completing one sends a real email. Pass subject and body to send something other than the drafted email, or omit them to send the draft. The task stays pending until the send is confirmed, and a second attempt while one is in flight is a 409.
There is no bulk complete, deliberately. Completing a manual_email task sends a real email, so a bulk complete would send unreviewed drafts to strangers. Skip, snooze and reassign are available in bulk, up to 500 tasks; completing is one at a time.

8. Removing someone

Un-enrolling is POST /v2/sequencer/enrollments/{id}/finish. There is no DELETE — an enrollment is a record of what happened, so it is finished rather than destroyed and its timeline survives. Once finished, the same contact can be enrolled again later. To stop contacting an entire company at once — you booked a meeting and want everyone else left alone — use POST /v2/sequencer/enrollments/bulk/account. Send "dry_run": true first; it can touch a lot of people:
The response says which account it matched and how many enrollments it would act on (matched), with queued: false. Send it again without dry_run to act. Identify the account by contact, domain or company_name. scope: "team" covers every sequence and needs a team admin as user_id; scope: "sequence" with a sequence ID needs that sequence’s owner or an admin.

9. Find out why someone was not added

This is the counterpart to the enrollments list, and the first place to look when a batch adds fewer people than you sent. Each row has a machine-readable reason — policy, suppressed, already_enrolled, throttle_capacity, throttle_daily, contact_not_found or other — plus the original message in detail and, for policy, the rule. Filter with ?reason=policy. Rows are kept for 90 days, one per person: the latest attempt replaces earlier ones.

Staying in sync

Rather than polling, subscribe to webhooks in the dashboard under Settings → Connections, on the API & Webhooks tab. The Create Webhook form needs a name, a public http or https URL, at least one event and at least one call disposition. The disposition filter applies only to CALL_LOGGED; Sequencer events are delivered whatever dispositions you pick. The Sequencer events are: Disabling, enabling or archiving a sequence pauses or resumes its enrollments in bulk and sends no per-enrollment events; re-read the enrollments after one of those. Each delivery is a POST with a JSON body:
  • contact is expanded so you know who the event is about without a second call. It carries id, first_name, last_name, email, title, and company (the ID of the linked company record) where the contact has them. It is null when the event has no contact, and only id when the contact no longer exists.
  • details is event-specific and can change as fields are added; treat every key in it as optional. The example shows two of the keys a reply carries.
  • task_id is filled for task events the API tier writes. For task events the cadence engine writes it is null and the task’s ID is in details.task, so read both.
Subscriptions created since signing was introduced also carry an x-salesfinity-signature header: sha256= followed by the hex HMAC-SHA256 of the raw request body. The dashboard does not show the signing secret yet, so the signature cannot be checked today. Until it can, put a long random token in the webhook URL and reject any request that does not carry it. Make your receiver idempotent. A delivery that fails with a 5xx, a 429, a timeout or another transport fault is retried, up to 3 attempts in total, after about half a second and then about one second, plus jitter. The same event can therefore arrive more than once — for example if your endpoint times out after it has already processed the payload. Every delivery carries an x-salesfinity-event-id header that is the same on every attempt for one event: record it, and skip any event whose ID you have already processed. Any other 4xx is not retried: the event is dropped and recorded on the webhook’s delivery log, which you can read in the dashboard. Each attempt is bounded at 10 seconds, so return a 2xx quickly and do your work afterwards. All attempts happen within about 30 seconds, so an endpoint that is down for longer misses those events. Deliveries are independent per subscription and are not ordered, so do not infer sequence from arrival order; compare occurred_at, and reconcile with the API — for example GET /v2/sequencer/enrollments/{id}/events — after an outage.

Rate limits and errors

Sequencer calls have their own limits, shared by every API key on your team: 600 requests per minute for reads (GET) and 300 for writes (POST, PATCH, PUT and DELETE). Over a limit you get a 429 with a Retry-After header giving the seconds to wait. Responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, so a bulk job can slow down before it hits the limit. See Rate limits. Retry 429 and 5xx with exponential backoff and jitter, sending the same Idempotency-Key. A failed request frees its key, and after a 504 the Sequencer may still finish the work, so before retrying a create that failed with a 5xx, read back whether it happened. 400, 403, 404 and 413 will not succeed on an identical retry, and neither will a 422 or a 409 caused by the record’s state until that state changes. Every error uses the same envelope as the rest of the API — see Errors.

Next steps

Browse the Sequencer endpoints

Every Sequencer operation, with request and response shapes.

Use the Sequencer from Claude

The same actions as MCP tools for an AI assistant.

Handle API errors

Statuses, retries and idempotency keys.

Learn the Sequencer in the app

How sequences, steps and rules work for reps.