Preview an enrollment run
Shows what Start an enrollment run would do with the same body, without doing it. Nothing is enrolled, no contact is created or changed, and no refusal is recorded. The response has a preview_id, counts across the whole set, a per-reason breakdown of everyone excluded, and up to 200 sample rows, with the rows that need explaining first: exclusions, then approvals, then the people who would be added.
Anything Salesfinity can answer from data it already holds is answered exactly: the suppression list, live enrollments in this sequence, the sequence’s per-owner throttles, and rules evaluated on the contact’s stored data and last CRM snapshot. A rule that would need a live CRM read is listed under unmeasured rather than guessed, and is checked as each person is actually added. Rules using the active_in_other_sequence condition (“Already in another sequence”) are not applied in the preview, so a run can refuse people the preview showed as will_add.
To enroll exactly the reviewed set, start a run with source.kind preview, the preview_id, and the same sequence and owner_id: the preview stores the people, not the sequence. To add people a rule blocked, pass the key of each row with overridable: true in force_include; they are added and held for approval. Previews expire after 30 minutes, and starting a run from an expired one returns 400.
Two options add detail, and change nothing a run would do. include_routing: true reports people a rule would start in another sequence instead under their own status, routed: each such row has routed_to (sequence, name, rule_name), counts.routed counts them, and routed_reasons groups them by target sequence and rule. It also adds the excluded_no_phone status, for people with no number to dial when every step is a call. Without it, both kinds come back as excluded_rule, counted in counts.excluded. include_call_history: true adds each row’s call_history (all their dials on record: attempts, answered, last_result_label, last_attempt_at; null when there is no contact on file) and calls_before, which buckets the whole set by dials in the last 90 days. A run refuses both options.
The request is validated the same way as a run, so the same 4xx responses apply. force_include is ignored here. Takes no Idempotency-Key. Counts toward the Sequencer writes rate limit, even though it changes nothing.
Reading the preview
countscovers everyone:totalequalswill_addplusneeds_approvalplusexcluded, plusroutedwithinclude_routing.excluded_reasonsbreaksexcludeddown by reason, again across everyone.rowsis a sample of up to 200 people, with the ones that need explaining first: exclusions, then approvals, then people who would be added.rows_truncatedis true when there were more.truncatedis true when the source had more than 5,000 usable people; a run takes only the first 5,000.
Routing and call history
Two options add detail to the preview. Neither changes what a run would do, and a run refuses both.include_routing: true shows people a rule would start in another sequence under their own status:
- A row with status
routedhasrouted_to: thesequence, itsnameand therule_name. It isoverridable: put itskeyinforce_includeto add them here anyway, held for approval. counts.routedcounts them, androuted_reasonsgroups them by target sequence and rule.- It also adds the
excluded_no_phonestatus, for people with no number to dial when every step is a call.
excluded_rule and are counted in counts.excluded, and
excluded_reasons lists the routed ones as A rule starts them in another sequence instead. A routed row keeps
overridable: true; a row with no number to dial has overridable: false, because no rule decided it.
include_call_history: true adds:
call_historyon each row:attempts,answered,last_result_labelandlast_attempt_at, counting every dial to the person on record, from any list. It isnullwhen there is no contact on file.calls_beforefor the whole set: how many people were dialednone,one_two,three_fiveorsix_plustimes in the last 90 days (days: 90).
What the preview cannot see
Anything answerable from data Salesfinity already holds is answered exactly. A few things are not:- A rule that needs a live CRM read is listed under
unmeasuredrather than guessed. It is checked as each person is actually added. - Rules using the
active_in_other_sequencecondition (“Already in another sequence”) are not applied, so a run can refuse people the preview showed aswill_add. - A contact id that is not on your team is treated as a new person, so it usually shows as
will_add; the run skips it asContact not found. - Duplicates inside the batch are not detected; the run enrolls the first and refuses the rest as already enrolled.
From preview to run
Previews last 30 minutes. To enroll exactly the reviewed set, start a run from it with the samesequence and owner_id, because the preview stores the people, not the sequence:
force_include takes the key of rows with overridable: true: people a rule blocks, or would start in another sequence. Those people are added here and held for approval rather than started, so a manager still decides. Starting from a preview that has expired returns 400.
The preview takes no Idempotency-Key, ignores force_include, and counts toward the Sequencer writes rate limit even though it changes nothing.Authorizations
Team-scoped API key generated in the Salesfinity dashboard under Settings → Connections → API & Webhooks. A missing or invalid key returns HTTP 403.
Body
The team member this call acts as. Must be an active member of the team the API key belongs to.
"507f1f77bcf86cd799439011"
The sequence to enroll into. It can be off; it cannot be archived, have enrollment paused, or have no steps.
Who owns the resulting enrollments and gets their tasks. Defaults to user_id. Must be a member of the team; otherwise 422.
Preview row keys of people to add even though a rule of engagement blocks them or would start them in another sequence. They are enrolled and held for approval, not started. Only a rule can be overridden: the suppression list, a live enrollment and the throttles still apply, and keys of people no rule stops change nothing. At most 5,000. Ignored here; send it when you start the run.
5000Also report people a rule would start in another sequence instead: the routed status with routed_to, counts.routed and routed_reasons, and the excluded_no_phone status. Without it the same people come back as excluded_rule, counted in counts.excluded. What a run does is the same either way.
Also return each row's call_history (all their dials on record, from any list: attempts, answered, last_result_label, last_attempt_at; null when there is no contact on file) and calls_before, the whole set bucketed by dials in the last 90 days.
Response
What the run would do.
A dry run. Anything answerable from held data is exact; anything needing a live CRM read is reported under unmeasured rather than guessed.
pv_ followed by 24 hexadecimal characters. Pass it back as a preview source to enroll exactly this set. Expires after 30 minutes.
"pv_3e7a9c1b5d2f4e6a8c0b2d4f"
Totals across the whole set, not only the sample rows. total equals will_add plus needs_approval plus excluded, plus routed with include_routing.
How many were excluded for each reason, across the whole set. Only reasons with at least one person are listed. Without include_routing, people a rule would start in another sequence are listed here under excluded_rule, as A rule starts them in another sequence instead.
With include_routing only: the people a rule would start in another sequence, grouped by that sequence and rule.
Up to 200 sample rows: exclusions first, then people held for approval, then people who would be added.
True when there were more than 200 rows and only the first 200 are returned. The counts still cover everyone.
The source had more than 5,000 usable people; a run would consider only the first 5,000.
Rules that could not be evaluated without a live CRM read. They are checked as each person is actually added.
With include_call_history only: everyone in the set, bucketed by how many times they were dialed in the last 90 days.