curl --request POST \
--url https://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/preview \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"delays": [
{
"step": 100,
"delay_days": 182
}
],
"scope": "new_and_unstarted",
"expected_updated_at": "2023-11-07T05:31:56Z"
}
'import requests
url = "https://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/preview"
payload = {
"delays": [
{
"step": 100,
"delay_days": 182
}
],
"scope": "new_and_unstarted",
"expected_updated_at": "2023-11-07T05:31:56Z"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
delays: [{step: 100, delay_days: 182}],
scope: 'new_and_unstarted',
expected_updated_at: '2023-11-07T05:31:56Z'
})
};
fetch('https://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/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://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/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([
'delays' => [
[
'step' => 100,
'delay_days' => 182
]
],
'scope' => 'new_and_unstarted',
'expected_updated_at' => '2023-11-07T05:31:56Z'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/preview"
payload := strings.NewReader("{\n \"delays\": [\n {\n \"step\": 100,\n \"delay_days\": 182\n }\n ],\n \"scope\": \"new_and_unstarted\",\n \"expected_updated_at\": \"2023-11-07T05:31:56Z\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/preview")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"delays\": [\n {\n \"step\": 100,\n \"delay_days\": 182\n }\n ],\n \"scope\": \"new_and_unstarted\",\n \"expected_updated_at\": \"2023-11-07T05:31:56Z\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"delays\": [\n {\n \"step\": 100,\n \"delay_days\": 182\n }\n ],\n \"scope\": \"new_and_unstarted\",\n \"expected_updated_at\": \"2023-11-07T05:31:56Z\"\n}"
response = http.request(request)
puts response.read_body{
"campaign_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "draft",
"version": "2023-11-07T05:31:56Z",
"scope": "new_and_unstarted",
"applied": true,
"cohorts": {
"leads_not_started": 0,
"leads_in_flight": 0,
"leads_waiting_acceptance": 0,
"leads_sending_now": 0,
"leads_finished": 0,
"earliest_in_flight_step": 123,
"leads_pinned": 0,
"leads_already_pinned": 0,
"leads_protection_unknown": 0
},
"already_applied": false,
"version_conflict": false,
"blocking": [
"<string>"
],
"changes": [
{
"step": 123,
"channel": "<string>",
"delay_days": 123,
"is_change": true,
"action_type": "<string>",
"current_delay_days": 123,
"current_delay_malformed": false,
"leads_affected": 0,
"leads_protected": 0,
"leads_waiting_acceptance_protected": 0,
"leads_past_step": 0
}
],
"steps": [
{
"step": 123,
"channel": "<string>",
"editable": true,
"action_type": "<string>",
"delay_days": 123,
"stored_delay_days": 123
}
],
"notes": [
"<string>"
]
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Preview Campaign Step Delays
Preview a wait (delay) change on a launched campaign — NO write.
Reports, per changed step, how many not-yet-started leads will USE the new
wait and how many people already in the sequence KEEP their current one, plus
blocking reasons an apply would refuse right now (stale version, a send in
progress). A stale version or a busy campaign never fails this call — they are
reported so the UI can render an honest confirm dialog. Org-scoped like every
other campaign write path.
curl --request POST \
--url https://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/preview \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"delays": [
{
"step": 100,
"delay_days": 182
}
],
"scope": "new_and_unstarted",
"expected_updated_at": "2023-11-07T05:31:56Z"
}
'import requests
url = "https://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/preview"
payload = {
"delays": [
{
"step": 100,
"delay_days": 182
}
],
"scope": "new_and_unstarted",
"expected_updated_at": "2023-11-07T05:31:56Z"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
delays: [{step: 100, delay_days: 182}],
scope: 'new_and_unstarted',
expected_updated_at: '2023-11-07T05:31:56Z'
})
};
fetch('https://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/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://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/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([
'delays' => [
[
'step' => 100,
'delay_days' => 182
]
],
'scope' => 'new_and_unstarted',
'expected_updated_at' => '2023-11-07T05:31:56Z'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/preview"
payload := strings.NewReader("{\n \"delays\": [\n {\n \"step\": 100,\n \"delay_days\": 182\n }\n ],\n \"scope\": \"new_and_unstarted\",\n \"expected_updated_at\": \"2023-11-07T05:31:56Z\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/preview")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"delays\": [\n {\n \"step\": 100,\n \"delay_days\": 182\n }\n ],\n \"scope\": \"new_and_unstarted\",\n \"expected_updated_at\": \"2023-11-07T05:31:56Z\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/v1/campaigns/{campaign_id}/step-delays/preview")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"delays\": [\n {\n \"step\": 100,\n \"delay_days\": 182\n }\n ],\n \"scope\": \"new_and_unstarted\",\n \"expected_updated_at\": \"2023-11-07T05:31:56Z\"\n}"
response = http.request(request)
puts response.read_body{
"campaign_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"status": "draft",
"version": "2023-11-07T05:31:56Z",
"scope": "new_and_unstarted",
"applied": true,
"cohorts": {
"leads_not_started": 0,
"leads_in_flight": 0,
"leads_waiting_acceptance": 0,
"leads_sending_now": 0,
"leads_finished": 0,
"earliest_in_flight_step": 123,
"leads_pinned": 0,
"leads_already_pinned": 0,
"leads_protection_unknown": 0
},
"already_applied": false,
"version_conflict": false,
"blocking": [
"<string>"
],
"changes": [
{
"step": 123,
"channel": "<string>",
"delay_days": 123,
"is_change": true,
"action_type": "<string>",
"current_delay_days": 123,
"current_delay_malformed": false,
"leads_affected": 0,
"leads_protected": 0,
"leads_waiting_acceptance_protected": 0,
"leads_past_step": 0
}
],
"steps": [
{
"step": 123,
"channel": "<string>",
"editable": true,
"action_type": "<string>",
"delay_days": 123,
"stored_delay_days": 123
}
],
"notes": [
"<string>"
]
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
Body
Request body for the delay preview and the delay apply routes.
Requested wait changes (unique step numbers).
1 - 20 elementsShow child attributes
Show child attributes
Cohort the edit applies to. Only 'new_and_unstarted' exists: already-started leads always keep the waits they were enrolled under.
new_and_unstarted The campaign's updated_at the edit was composed against. REQUIRED on apply (optimistic concurrency); optional on preview, where a mismatch is reported as version_conflict instead of failing.
Response
Successful Response
Preview/apply result for a launched-campaign delay edit.
Campaign ID
Campaign status at read time
draft, launching, scoring, review_ready, scheduled, active, draining, paused, completed, failed campaigns.updated_at AFTER this call; send back as expected_updated_at.
Applied scope (echo)
new_and_unstarted True only when this call wrote the new waits
Campaign-wide cohort snapshot
Show child attributes
Show child attributes
True when the stored sequence already carried the requested waits, so nothing was written. On an apply the version CAS has already passed, so it means 'nothing has changed since the version you sent' — it is NOT a receipt that an earlier submission of yours committed.
Preview only: expected_updated_at is no longer the current version.
Machine reasons an apply would refuse right now ('version_conflict', 'send_in_progress', 'protection_incomplete', 'protection_unknown'); empty means clear.
Per-step analysis of the request
Show child attributes
Show child attributes
The full sequence. delay_days is the proposed sequence on a preview and the persisted one on an apply; stored_delay_days is always what is persisted.
Show child attributes
Show child attributes
Short human-readable strings safe to render verbatim.