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 auser_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
_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:{{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.
4. Preview, then enroll what you reviewed
Preview is free, changes nothing, and tells you who would actually go in: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:
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: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:
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 anIdempotency-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
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
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:
- Call tasks. Pass the
call_logthe 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_emailtasks. Completing one sends a real email. Passsubjectandbodyto send something other than the drafted email, or omit them to send the draft. The task stayspendinguntil the send is confirmed, and a second attempt while one is in flight is a 409.
8. Removing someone
Un-enrolling isPOST /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:
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
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 publichttp 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:
contactis expanded so you know who the event is about without a second call. It carriesid,first_name,last_name,email,title, andcompany(the ID of the linked company record) where the contact has them. It isnullwhen the event has no contact, and onlyidwhen the contact no longer exists.detailsis 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_idis filled for task events the API tier writes. For task events the cadence engine writes it isnulland the task’s ID is indetails.task, so read both.
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.