curl --request POST \
--url https://client-api.salesfinity.co/v2/sequencer/policies/preview \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"user_id": "64b7f0c2e4b0a1b2c3d4e5f9",
"rules": [
{
"name": "Do not enroll contacts with an open deal",
"condition_group": {
"op": "all",
"children": [
{
"type": "crm_open_deal",
"params": {}
}
]
},
"action": {
"type": "block"
},
"scope": [
"enrollment"
]
},
{
"name": "Warn when another rep is already working the contact",
"condition_group": {
"op": "all",
"children": [
{
"type": "active_in_other_sequence",
"params": {
"mode": "other_rep"
}
}
]
},
"action": {
"type": "warn"
},
"scope": [
"enrollment"
]
}
]
}
'import requests
url = "https://client-api.salesfinity.co/v2/sequencer/policies/preview"
payload = {
"user_id": "64b7f0c2e4b0a1b2c3d4e5f9",
"rules": [
{
"name": "Do not enroll contacts with an open deal",
"condition_group": {
"op": "all",
"children": [
{
"type": "crm_open_deal",
"params": {}
}
]
},
"action": { "type": "block" },
"scope": ["enrollment"]
},
{
"name": "Warn when another rep is already working the contact",
"condition_group": {
"op": "all",
"children": [
{
"type": "active_in_other_sequence",
"params": { "mode": "other_rep" }
}
]
},
"action": { "type": "warn" },
"scope": ["enrollment"]
}
]
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
user_id: '64b7f0c2e4b0a1b2c3d4e5f9',
rules: [
{
name: 'Do not enroll contacts with an open deal',
condition_group: {op: 'all', children: [{type: 'crm_open_deal', params: {}}]},
action: {type: 'block'},
scope: ['enrollment']
},
{
name: 'Warn when another rep is already working the contact',
condition_group: {
op: 'all',
children: [{type: 'active_in_other_sequence', params: {mode: 'other_rep'}}]
},
action: {type: 'warn'},
scope: ['enrollment']
}
]
})
};
fetch('https://client-api.salesfinity.co/v2/sequencer/policies/preview', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://client-api.salesfinity.co/v2/sequencer/policies/preview",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'user_id' => '64b7f0c2e4b0a1b2c3d4e5f9',
'rules' => [
[
'name' => 'Do not enroll contacts with an open deal',
'condition_group' => [
'op' => 'all',
'children' => [
[
'type' => 'crm_open_deal',
'params' => [
]
]
]
],
'action' => [
'type' => 'block'
],
'scope' => [
'enrollment'
]
],
[
'name' => 'Warn when another rep is already working the contact',
'condition_group' => [
'op' => 'all',
'children' => [
[
'type' => 'active_in_other_sequence',
'params' => [
'mode' => 'other_rep'
]
]
]
],
'action' => [
'type' => 'warn'
],
'scope' => [
'enrollment'
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://client-api.salesfinity.co/v2/sequencer/policies/preview"
payload := strings.NewReader("{\n \"user_id\": \"64b7f0c2e4b0a1b2c3d4e5f9\",\n \"rules\": [\n {\n \"name\": \"Do not enroll contacts with an open deal\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"crm_open_deal\",\n \"params\": {}\n }\n ]\n },\n \"action\": {\n \"type\": \"block\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n },\n {\n \"name\": \"Warn when another rep is already working the contact\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"active_in_other_sequence\",\n \"params\": {\n \"mode\": \"other_rep\"\n }\n }\n ]\n },\n \"action\": {\n \"type\": \"warn\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://client-api.salesfinity.co/v2/sequencer/policies/preview")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"user_id\": \"64b7f0c2e4b0a1b2c3d4e5f9\",\n \"rules\": [\n {\n \"name\": \"Do not enroll contacts with an open deal\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"crm_open_deal\",\n \"params\": {}\n }\n ]\n },\n \"action\": {\n \"type\": \"block\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n },\n {\n \"name\": \"Warn when another rep is already working the contact\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"active_in_other_sequence\",\n \"params\": {\n \"mode\": \"other_rep\"\n }\n }\n ]\n },\n \"action\": {\n \"type\": \"warn\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://client-api.salesfinity.co/v2/sequencer/policies/preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"user_id\": \"64b7f0c2e4b0a1b2c3d4e5f9\",\n \"rules\": [\n {\n \"name\": \"Do not enroll contacts with an open deal\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"crm_open_deal\",\n \"params\": {}\n }\n ]\n },\n \"action\": {\n \"type\": \"block\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n },\n {\n \"name\": \"Warn when another rep is already working the contact\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"active_in_other_sequence\",\n \"params\": {\n \"mode\": \"other_rep\"\n }\n }\n ]\n },\n \"action\": {\n \"type\": \"warn\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"sampled": 186,
"available": 1240,
"verdicts": {
"allow": 151,
"warn": 19,
"block": 16,
"require_approval": 0
},
"by_rule": [
{
"rule_id": "draft-0",
"rule_name": "Do not enroll contacts with an open deal",
"action": "block",
"matched": 16
},
{
"rule_id": "draft-1",
"rule_name": "Warn when another rep is already working the contact",
"action": "warn",
"matched": 19
}
],
"examples": [
{
"name": "Theresa Webb",
"verdict": "block",
"rule_name": "Do not enroll contacts with an open deal"
}
],
"unmeasured": [],
"crm_reads": []
}{
"message": [
"rules.0.scope must contain at least 1 elements"
],
"error": "Bad Request",
"statusCode": 400,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}{
"message": "The acting user is not an active member of this team",
"error": "Forbidden",
"statusCode": 403,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}{
"message": "Rate limit of 300 requests per minute exceeded. Retry in 12s.",
"error": "Too Many Requests",
"statusCode": 429,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}{
"message": "Internal server error",
"error": "Internal Server Error",
"statusCode": 500,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}{
"message": "Sequencer service unreachable",
"error": "Bad Gateway",
"statusCode": 502,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}{
"message": "Sequencer service timed out",
"error": "Gateway Timeout",
"statusCode": 504,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}Preview a draft rule set
Measures what an unsaved set of scenarios would have done to the prospects behind the team’s 200 most recent enrollments, without saving or enrolling anything.
Run it before saving: a broad rule on the default policy blocks enrollment on every sequence that uses it. Only enabled rules whose scope includes enrollment are measured; step rules are not. The evaluation uses the CRM data already stored for each prospect and makes no CRM calls, so a rule that reads a CRM field not yet fetched for those prospects is listed in unmeasured rather than counted as allowing them. Preview checks the structure of the rules but not whether each action fits its scope; create and update check both.
Changes nothing, so it takes no Idempotency-Key. It still requires user_id, and as a POST it counts against the Sequencer writes rate limit.
curl --request POST \
--url https://client-api.salesfinity.co/v2/sequencer/policies/preview \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"user_id": "64b7f0c2e4b0a1b2c3d4e5f9",
"rules": [
{
"name": "Do not enroll contacts with an open deal",
"condition_group": {
"op": "all",
"children": [
{
"type": "crm_open_deal",
"params": {}
}
]
},
"action": {
"type": "block"
},
"scope": [
"enrollment"
]
},
{
"name": "Warn when another rep is already working the contact",
"condition_group": {
"op": "all",
"children": [
{
"type": "active_in_other_sequence",
"params": {
"mode": "other_rep"
}
}
]
},
"action": {
"type": "warn"
},
"scope": [
"enrollment"
]
}
]
}
'import requests
url = "https://client-api.salesfinity.co/v2/sequencer/policies/preview"
payload = {
"user_id": "64b7f0c2e4b0a1b2c3d4e5f9",
"rules": [
{
"name": "Do not enroll contacts with an open deal",
"condition_group": {
"op": "all",
"children": [
{
"type": "crm_open_deal",
"params": {}
}
]
},
"action": { "type": "block" },
"scope": ["enrollment"]
},
{
"name": "Warn when another rep is already working the contact",
"condition_group": {
"op": "all",
"children": [
{
"type": "active_in_other_sequence",
"params": { "mode": "other_rep" }
}
]
},
"action": { "type": "warn" },
"scope": ["enrollment"]
}
]
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
user_id: '64b7f0c2e4b0a1b2c3d4e5f9',
rules: [
{
name: 'Do not enroll contacts with an open deal',
condition_group: {op: 'all', children: [{type: 'crm_open_deal', params: {}}]},
action: {type: 'block'},
scope: ['enrollment']
},
{
name: 'Warn when another rep is already working the contact',
condition_group: {
op: 'all',
children: [{type: 'active_in_other_sequence', params: {mode: 'other_rep'}}]
},
action: {type: 'warn'},
scope: ['enrollment']
}
]
})
};
fetch('https://client-api.salesfinity.co/v2/sequencer/policies/preview', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://client-api.salesfinity.co/v2/sequencer/policies/preview",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'user_id' => '64b7f0c2e4b0a1b2c3d4e5f9',
'rules' => [
[
'name' => 'Do not enroll contacts with an open deal',
'condition_group' => [
'op' => 'all',
'children' => [
[
'type' => 'crm_open_deal',
'params' => [
]
]
]
],
'action' => [
'type' => 'block'
],
'scope' => [
'enrollment'
]
],
[
'name' => 'Warn when another rep is already working the contact',
'condition_group' => [
'op' => 'all',
'children' => [
[
'type' => 'active_in_other_sequence',
'params' => [
'mode' => 'other_rep'
]
]
]
],
'action' => [
'type' => 'warn'
],
'scope' => [
'enrollment'
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://client-api.salesfinity.co/v2/sequencer/policies/preview"
payload := strings.NewReader("{\n \"user_id\": \"64b7f0c2e4b0a1b2c3d4e5f9\",\n \"rules\": [\n {\n \"name\": \"Do not enroll contacts with an open deal\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"crm_open_deal\",\n \"params\": {}\n }\n ]\n },\n \"action\": {\n \"type\": \"block\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n },\n {\n \"name\": \"Warn when another rep is already working the contact\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"active_in_other_sequence\",\n \"params\": {\n \"mode\": \"other_rep\"\n }\n }\n ]\n },\n \"action\": {\n \"type\": \"warn\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://client-api.salesfinity.co/v2/sequencer/policies/preview")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"user_id\": \"64b7f0c2e4b0a1b2c3d4e5f9\",\n \"rules\": [\n {\n \"name\": \"Do not enroll contacts with an open deal\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"crm_open_deal\",\n \"params\": {}\n }\n ]\n },\n \"action\": {\n \"type\": \"block\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n },\n {\n \"name\": \"Warn when another rep is already working the contact\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"active_in_other_sequence\",\n \"params\": {\n \"mode\": \"other_rep\"\n }\n }\n ]\n },\n \"action\": {\n \"type\": \"warn\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://client-api.salesfinity.co/v2/sequencer/policies/preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"user_id\": \"64b7f0c2e4b0a1b2c3d4e5f9\",\n \"rules\": [\n {\n \"name\": \"Do not enroll contacts with an open deal\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"crm_open_deal\",\n \"params\": {}\n }\n ]\n },\n \"action\": {\n \"type\": \"block\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n },\n {\n \"name\": \"Warn when another rep is already working the contact\",\n \"condition_group\": {\n \"op\": \"all\",\n \"children\": [\n {\n \"type\": \"active_in_other_sequence\",\n \"params\": {\n \"mode\": \"other_rep\"\n }\n }\n ]\n },\n \"action\": {\n \"type\": \"warn\"\n },\n \"scope\": [\n \"enrollment\"\n ]\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"sampled": 186,
"available": 1240,
"verdicts": {
"allow": 151,
"warn": 19,
"block": 16,
"require_approval": 0
},
"by_rule": [
{
"rule_id": "draft-0",
"rule_name": "Do not enroll contacts with an open deal",
"action": "block",
"matched": 16
},
{
"rule_id": "draft-1",
"rule_name": "Warn when another rep is already working the contact",
"action": "warn",
"matched": 19
}
],
"examples": [
{
"name": "Theresa Webb",
"verdict": "block",
"rule_name": "Do not enroll contacts with an open deal"
}
],
"unmeasured": [],
"crm_reads": []
}{
"message": [
"rules.0.scope must contain at least 1 elements"
],
"error": "Bad Request",
"statusCode": 400,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}{
"message": "The acting user is not an active member of this team",
"error": "Forbidden",
"statusCode": 403,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}{
"message": "Rate limit of 300 requests per minute exceeded. Retry in 12s.",
"error": "Too Many Requests",
"statusCode": 429,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}{
"message": "Internal server error",
"error": "Internal Server Error",
"statusCode": 500,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}{
"message": "Sequencer service unreachable",
"error": "Bad Gateway",
"statusCode": 502,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}{
"message": "Sequencer service timed out",
"error": "Gateway Timeout",
"statusCode": 504,
"request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
}Reading the result
{
"sampled": 186,
"available": 1240,
"verdicts": { "allow": 151, "warn": 19, "block": 16, "require_approval": 0 },
"by_rule": [
{ "rule_id": "draft-0", "rule_name": "Do not enroll contacts with an open deal", "action": "block", "matched": 16 },
{ "rule_id": "draft-1", "rule_name": "Warn when another rep is already working the contact", "action": "warn", "matched": 19 }
],
"examples": [{ "name": "Theresa Webb", "verdict": "block", "rule_name": "Do not enroll contacts with an open deal" }],
"unmeasured": [],
"crm_reads": []
}
unmeasured reads a CRM field that has not been fetched for these prospects yet, so the count says nothing about it.
Good to know
- Only enabled rules with
enrollmentinscopeare measured. Step rules are not. - Preview uses CRM data already stored and makes no CRM calls.
- Preview checks the shape of each rule but not whether its action fits its scope. Create and update check both, so a rule that previews can still be refused on save.
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 draft scenarios to measure, up to 100. Not saved. Only enabled rules with enrollment in scope are measured.
100Show child attributes
Show child attributes
Response
What the draft would have done.
Prospects evaluated: the distinct contacts behind the 200 most recent enrollments.
Enrollments the team has ever had, so a thin sample is visibly thin.
How many sampled prospects each verdict would have applied to.
Show child attributes
Show child attributes
Per measured rule, in policy order. A prospect can match several rules.
Show child attributes
Show child attributes
Up to 5 prospects who would not simply have been allowed.
Show child attributes
Show child attributes
Rules that could not be measured, with why.
Show child attributes
Show child attributes
The CRM fields the policy will read per prospect once live.
Show child attributes
Show child attributes