curl --request POST \
--url https://api.goyappr.com/campaigns/{id}/launch \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.goyappr.com/campaigns/{id}/launch"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.goyappr.com/campaigns/{id}/launch', 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.goyappr.com/campaigns/{id}/launch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.goyappr.com/campaigns/{id}/launch"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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.goyappr.com/campaigns/{id}/launch")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.goyappr.com/campaigns/{id}/launch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"company_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "July reactivation",
"description": "<string>",
"status": "draft",
"agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agent": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"type": "prompt",
"flow_config": {
"nodes": [
{
"id": "<string>",
"type": "start",
"name": "<string>",
"position": {
"x": 123,
"y": 123
},
"agent_speaks_first": true,
"greeting": "<string>",
"is_literal": false,
"next_step_id": "<string>",
"auto_advance": true
}
],
"flow_config_version": "1",
"metadata": {
"custom_metadata_keys": [
"<string>"
]
}
},
"system_prompt": "<string>",
"description": "<string>",
"voice": "Michal",
"background_sound": "call_center",
"background_sound_volume": 0.3,
"language": "he",
"temperature": 1,
"greeting_message": "<string>",
"agent_speaks_first": true,
"vad_stop_secs": 0.5,
"vad_start_secs": 0.2,
"vad_confidence": 0.7,
"silence_timeout_secs": 60,
"max_continuous_speech_secs": 120,
"max_call_duration_secs": 600,
"lead_memory_enabled": true,
"is_active": true,
"webhook_url": "<string>",
"webhook_events": [
"call.started"
],
"webhook_headers": {
"Authorization": "Bearer sk_live_…",
"X-Source": "yappr"
},
"extraction_parameters": [
{
"name": "customerName",
"description": "The caller's full name as mentioned during the conversation"
}
],
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z"
},
"from_phone_number_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"from_phone_number": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"number": "+972551234567",
"friendly_name": "<string>",
"provider": "<string>",
"status": "active",
"is_active": true,
"inbound_agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"outbound_agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"sip_inbound_configured": true,
"sip_outbound_configured": true,
"country_code": "<string>",
"monthly_cost": 123,
"created_at": "2023-11-07T05:31:56Z",
"carrier_account": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"provider": "telnyx",
"status": "untested"
},
"ownership_verified_at": "2023-11-07T05:31:56Z"
},
"from_number": "+972551234567",
"retry_rules": {},
"calling_window": {},
"stop_disposition_ids": [
"3c90c3cc-0d44-4b50-8888-8dd25736052a"
],
"stop_dispositions": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "<string>",
"color": "#22c55e",
"position": 123,
"is_protected": true,
"created_at": "2023-11-07T05:31:56Z"
}
],
"stop_on_no_answer": false,
"stop_on_voicemail": false,
"stop_on_unclassified": false,
"max_attempts": 3,
"max_infra_retries": 3,
"retry_no_answer_seconds": 3600,
"retry_completed_seconds": 86400,
"randomize_retry_time": false,
"double_dial_enabled": false,
"double_dial_gap_seconds": 90,
"max_calls_per_day": 200,
"min_seconds_between_calls": 30,
"max_in_flight": 2,
"budget_cents": 2,
"regulatory_basis": "lawful_basis_confirmed",
"starts_at": "2023-11-07T05:31:56Z",
"ends_at": "2023-11-07T05:31:56Z",
"daily_admitted_count": 123,
"daily_window_date": "2023-12-25",
"last_admitted_at": "2023-11-07T05:31:56Z",
"estimate_cents": 123,
"spent_cents": 123,
"reserved_cents": 123,
"last_tick_at": "2023-11-07T05:31:56Z",
"last_tick_result": "<string>",
"last_error": "<string>",
"started_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"total_leads": 123,
"stats": {},
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"created_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": "<string>",
"code": "<string>"
}{
"error": "<string>",
"code": "<string>"
}{
"error": "ALREADY_IN_ACTIVE_CAMPAIGN",
"message": "<string>"
}Launch a campaign
Moves the campaign to running. Required scope campaigns:manage.
Preflight — all of these must hold, or the call returns 422 CAMPAIGN_NOT_READY
with the one thing to fix:
- an
agent_idis assigned, and that agent still exists - that agent has a positive
max_call_duration_secs, so the cost ceiling is bounded - a
from_phone_number_idis assigned and the number is still active regulatory_basisis set- every configuration field is set — none have defaults, so a field never sent is
still
null:max_attempts,max_infra_retries,retry_no_answer_seconds,retry_completed_seconds,randomize_retry_time,stop_on_no_answer,stop_on_voicemail,stop_on_unclassified,double_dial_enabled,double_dial_gap_seconds,max_calls_per_day,min_seconds_between_calls,max_in_flight - at least one stop rule is configured (
stop_disposition_ids,stop_on_no_answer,stop_on_voicemail) — otherwise the campaign would call every contact until the attempt cap - the workspace has an upcoming calling window
- at least one contact is enrolled and eligible
Launching is also how you leave any of the paused_* states. Launching an
already-running campaign is a no-op and returns 200.
curl --request POST \
--url https://api.goyappr.com/campaigns/{id}/launch \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.goyappr.com/campaigns/{id}/launch"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.goyappr.com/campaigns/{id}/launch', 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.goyappr.com/campaigns/{id}/launch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.goyappr.com/campaigns/{id}/launch"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
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.goyappr.com/campaigns/{id}/launch")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.goyappr.com/campaigns/{id}/launch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"company_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "July reactivation",
"description": "<string>",
"status": "draft",
"agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"agent": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"type": "prompt",
"flow_config": {
"nodes": [
{
"id": "<string>",
"type": "start",
"name": "<string>",
"position": {
"x": 123,
"y": 123
},
"agent_speaks_first": true,
"greeting": "<string>",
"is_literal": false,
"next_step_id": "<string>",
"auto_advance": true
}
],
"flow_config_version": "1",
"metadata": {
"custom_metadata_keys": [
"<string>"
]
}
},
"system_prompt": "<string>",
"description": "<string>",
"voice": "Michal",
"background_sound": "call_center",
"background_sound_volume": 0.3,
"language": "he",
"temperature": 1,
"greeting_message": "<string>",
"agent_speaks_first": true,
"vad_stop_secs": 0.5,
"vad_start_secs": 0.2,
"vad_confidence": 0.7,
"silence_timeout_secs": 60,
"max_continuous_speech_secs": 120,
"max_call_duration_secs": 600,
"lead_memory_enabled": true,
"is_active": true,
"webhook_url": "<string>",
"webhook_events": [
"call.started"
],
"webhook_headers": {
"Authorization": "Bearer sk_live_…",
"X-Source": "yappr"
},
"extraction_parameters": [
{
"name": "customerName",
"description": "The caller's full name as mentioned during the conversation"
}
],
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z"
},
"from_phone_number_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"from_phone_number": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"number": "+972551234567",
"friendly_name": "<string>",
"provider": "<string>",
"status": "active",
"is_active": true,
"inbound_agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"outbound_agent_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"sip_inbound_configured": true,
"sip_outbound_configured": true,
"country_code": "<string>",
"monthly_cost": 123,
"created_at": "2023-11-07T05:31:56Z",
"carrier_account": {
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"provider": "telnyx",
"status": "untested"
},
"ownership_verified_at": "2023-11-07T05:31:56Z"
},
"from_number": "+972551234567",
"retry_rules": {},
"calling_window": {},
"stop_disposition_ids": [
"3c90c3cc-0d44-4b50-8888-8dd25736052a"
],
"stop_dispositions": [
{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"label": "<string>",
"color": "#22c55e",
"position": 123,
"is_protected": true,
"created_at": "2023-11-07T05:31:56Z"
}
],
"stop_on_no_answer": false,
"stop_on_voicemail": false,
"stop_on_unclassified": false,
"max_attempts": 3,
"max_infra_retries": 3,
"retry_no_answer_seconds": 3600,
"retry_completed_seconds": 86400,
"randomize_retry_time": false,
"double_dial_enabled": false,
"double_dial_gap_seconds": 90,
"max_calls_per_day": 200,
"min_seconds_between_calls": 30,
"max_in_flight": 2,
"budget_cents": 2,
"regulatory_basis": "lawful_basis_confirmed",
"starts_at": "2023-11-07T05:31:56Z",
"ends_at": "2023-11-07T05:31:56Z",
"daily_admitted_count": 123,
"daily_window_date": "2023-12-25",
"last_admitted_at": "2023-11-07T05:31:56Z",
"estimate_cents": 123,
"spent_cents": 123,
"reserved_cents": 123,
"last_tick_at": "2023-11-07T05:31:56Z",
"last_tick_result": "<string>",
"last_error": "<string>",
"started_at": "2023-11-07T05:31:56Z",
"completed_at": "2023-11-07T05:31:56Z",
"total_leads": 123,
"stats": {},
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"created_by": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": "<string>",
"code": "<string>"
}{
"error": "<string>",
"code": "<string>"
}{
"error": "ALREADY_IN_ACTIVE_CAMPAIGN",
"message": "<string>"
}running so it starts offering calls. A preflight runs first: if anything would stop the campaign dialing, launch is refused with 422 CAMPAIGN_NOT_READY and a message naming the specific problem, rather than accepting the launch and going quiet.
Launch works from draft and from any paused status, so it doubles as a resume. POST /campaigns/{id}/resume is the same operation with a different audit label.
Required scope: campaigns:manage.
Path parameters
| Parameter | Type | Notes |
|---|---|---|
id | uuid | Campaign ID. Must belong to the API key’s workspace. |
POST /campaigns or PATCH /campaigns/{id} first.
Preflight checklist
All ten must hold. The first failure is the one you get back.| Requirement | Failure message |
|---|---|
agent_id is set | Assign an agent before launching |
from_phone_number_id is set | Assign a phone number to call from before launching |
regulatory_basis is set | regulatory_basis is required before launching. One of: consent, existing_customer, non_marketing, registry_screened |
| Every configuration field is set | Finish configuring the campaign before launching. Not set: … — the message names each one |
At least one stop rule — a non-empty stop_disposition_ids, or stop_on_no_answer or stop_on_voicemail | Configure at least one stop rule before launching, or the campaign will keep calling every contact until the attempt limit is reached |
| The assigned agent still exists | The assigned agent no longer exists |
The agent has a positive max_call_duration_secs | The assigned agent has no maximum call duration set. Campaigns require one so the cost ceiling is bounded. |
| The from-number is still active | The phone number assigned to this campaign is no longer active |
| Your workspace has an upcoming calling window | This workspace has no upcoming calling window. Update your calling hours before launching. |
At least one contact is enrolled and still callable (pending or scheduled) | Enroll at least one contact before launching |
null and this check names it: max_attempts, max_infra_retries, retry_no_answer_seconds, retry_completed_seconds, randomize_retry_time, stop_on_no_answer, stop_on_voicemail, stop_on_unclassified, double_dial_enabled, double_dial_gap_seconds, max_calls_per_day, min_seconds_between_calls, max_in_flight. Fill them with PATCH /campaigns/{id} and launch again.
An agent with max_call_duration_secs = 0 has no cap of its own: each call can run up to the platform’s 65-minute limit, which puts worst-case spend far above any budget and silently turns budget_cents into a no-op. That is why a positive cap is mandatory for campaigns specifically.
The full launch sequence
# 1. Create the draft.
curl -X POST "https://api.goyappr.com/campaigns" \
-H "Authorization: Bearer $YAPPR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "July renewals",
"agent_id": "7e8a91c1-0000-4c11-9a00-000000000001",
"from_phone_number_id": "2d9f4b6a-0000-4a3c-8b21-000000000002",
"regulatory_basis": "existing_customer",
"stop_disposition_ids": ["5c1d9a2e-0000-4b10-9f31-000000000010"],
"randomize_retry_time": true,
"max_attempts": 3,
"max_calls_per_day": 150,
"min_seconds_between_calls": 45,
"budget_cents": 50000
}'
# 2. Enroll contacts.
curl -X POST "https://api.goyappr.com/campaigns/CAMPAIGN_ID/leads" \
-H "Authorization: Bearer $YAPPR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"lead_ids": ["a1c3e5f7-0000-4b21-9c30-000000000021"],
"phone_numbers": [
{ "phone": "0501234567", "name": "Dana Levi" },
"+972521112222"
]
}'
# 3. Launch.
curl -X POST "https://api.goyappr.com/campaigns/CAMPAIGN_ID/launch" \
-H "Authorization: Bearer $YAPPR_API_KEY"
# 4. Watch it work.
curl "https://api.goyappr.com/campaigns/CAMPAIGN_ID/stats" \
-H "Authorization: Bearer $YAPPR_API_KEY"
What happens after launch
- The campaign is evaluated roughly once a minute. Each evaluation checks credit, workspace calling hours,
max_calls_per_day,min_seconds_between_calls,max_in_flightandbudget_cents, then starts as many calls as those limits allow. - Each call is an ordinary outbound call on the shared queue, with the same weight as one placed by
POST /calls. Pacing controls how fast a campaign offers calls; it never grants priority. started_atis stamped on the first launch and preserved across later pause/resume cycles.last_tick_resultexplains every quiet minute —spacing,daily_cap_reached,outside_call_window,max_in_flight,no_eligible_leads, and so on. It is the first thing to read when arunningcampaign appears to be doing nothing. Full vocabulary on the stats page.- The campaign auto-completes (
status: "completed",completed_atset) once no contact is in a live state.
paused_insufficient_credit (balance below the minimum needed to place a call, auto-resumes on top-up), paused_budget, paused_infra (transient capacity problem) or paused_config (inactive from-number, agent duration cap removed, no reachable calling window).
Already running
Launching a campaign that is alreadyrunning is not an error. The campaign is returned unchanged with an extra field:
{
"id": "b3f1c0d2-5a44-4f0e-9c11-7a2e8d3f0001",
"status": "running",
"message": "Already running",
"company_id": "fe493f11-0000-0000-0000-000000000001"
}
Example response
HTTP 200 with the full campaign object, same shape asGET /campaigns/{id}:
{
"id": "b3f1c0d2-5a44-4f0e-9c11-7a2e8d3f0001",
"company_id": "fe493f11-0000-0000-0000-000000000001",
"name": "July renewals",
"status": "running",
"agent_id": "7e8a91c1-0000-4c11-9a00-000000000001",
"from_phone_number_id": "2d9f4b6a-0000-4a3c-8b21-000000000002",
"regulatory_basis": "existing_customer",
"stop_disposition_ids": ["5c1d9a2e-0000-4b10-9f31-000000000010"],
"randomize_retry_time": true,
"max_attempts": 3,
"max_calls_per_day": 150,
"min_seconds_between_calls": 45,
"max_in_flight": 2,
"daily_admitted_count": 0,
"budget_cents": 50000,
"spent_cents": 0,
"reserved_cents": 0,
"last_tick_at": null,
"last_tick_result": null,
"started_at": "2026-07-28T09:31:02.554Z",
"completed_at": null,
"total_leads": 412,
"agent": { "id": "7e8a91c1-0000-4c11-9a00-000000000001", "name": "Renewals agent" },
"from_phone_number": { "id": "2d9f4b6a-0000-4a3c-8b21-000000000002", "phone_number": "+972737000000" },
"stop_dispositions": [
{ "id": "5c1d9a2e-0000-4b10-9f31-000000000010", "label": "Do Not Call" }
]
}
GET /campaigns/{id}.
Not-ready response
{
"error": "CAMPAIGN_NOT_READY",
"message": "regulatory_basis is required before launching. One of: consent, existing_customer, non_marketing, registry_screened"
}
PATCH /campaigns/{id} (or POST /campaigns/{id}/leads, or your calling hours) and call launch again. Nothing about the campaign changes on a refused launch.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | — | Campaign is completed, stopped or archived — those states cannot be launched. |
| 401 | INSUFFICIENT_SCOPE | API key lacks campaigns:manage. |
| 404 | — | No campaign with that ID in this workspace, or it has been archived. |
| 422 | CAMPAIGN_NOT_READY | A preflight check failed. message names the exact problem. |
Authorizations
Your Yappr API key (e.g. ypr_live_...). Generate one in the dashboard under Settings → API Keys.
Path Parameters
Response
Campaign is running
A paced bulk-outbound dialing job over enrolled contacts.
A campaign is an admission-only scheduler. It decides when each enrolled
contact becomes eligible, then performs one ordinary outbound call — the same
queue, the same priority, and the same concurrency limits as a call placed with
POST /calls. The pacing fields below only control how fast a campaign hands
calls to that queue; a campaign call never takes precedence over anything else.
Campaigns are always created as draft and are launched with
POST /campaigns/{id}/launch.
Two independent per-contact stop conditions apply, whichever fires first:
max_attempts, and the stop-disposition set (stop_disposition_ids plus the
stop_on_* booleans). Landing a stop disposition retires that contact
permanently; any other outcome retries until the attempt cap.
Fields marked Engine-owned are written by the campaign engine and are never
writable. Sending one on create or update returns 400, as does any field name
that isn't on the writable list — a misspelled stop_dispositions is rejected
rather than silently ignored, so you can never believe a kill switch is armed
when it is not.
"July reactivation"
Engine-owned. Move it with the transition endpoints
(launch / pause / resume / stop), never with PATCH.
draft — created, not dialing.
scheduled — waiting for starts_at.
running — admitting contacts.
paused — paused by you. A manual pause is never lifted automatically; it
survives a top-up.
paused_insufficient_credit — balance is below the minimum needed to place a
call. Auto-resumes shortly after the balance is topped up.
paused_budget — budget_cents reached.
paused_infra — repeated platform-side failures.
paused_config — a permanent configuration problem (from-number no longer
active, agent missing a call-duration cap, no reachable calling window).
completed — every enrolled contact reached a terminal state.
stopped — stopped by you. Terminal.
archived — soft-deleted by DELETE /campaigns/{id}. Terminal.
draft, scheduled, running, paused, paused_insufficient_credit, paused_budget, paused_infra, paused_config, completed, stopped, archived Agent that runs the calls. Required before launch.
Full Agent object for agent_id.
Show child attributes
Show child attributes
Number the campaign calls from. Required before launch, and must still be active at launch time.
Full PhoneNumber object for from_phone_number_id.
Show child attributes
Show child attributes
Engine-owned. E.164 snapshot of the from-number, kept for audit even if the number is later released.
"+972551234567"
Optional per-outcome retry overrides. Free-form object — the explicit max_attempts / retry_*_seconds fields cover the common cases.
Optional campaign-level narrowing of the workspace calling hours. Omit to inherit the workspace schedule (GET /call-windows). A campaign can only ever dial inside the workspace window — this field cannot widen it.
Disposition IDs that retire a contact permanently when assigned — the campaign's kill switch. Always IDs, never labels: labels are renameable, and a rename would silently disarm the rule.
Do not list No Answer, Failed or Voicemail here. Those three are also
auto-assigned to calls nobody picked up, so using them as stop dispositions
retires contacts you did in fact speak to. Use stop_on_no_answer and
stop_on_voicemail instead.
Must contain dispositions belonging to your company; anything else is rejected
with 400.
Full Disposition objects for stop_disposition_ids.
Show child attributes
Show child attributes
Retire a contact the first time a call goes unanswered.
false
Retire a contact the first time a call reaches an answering machine.
false
What to do when the outcome that arrives is Unclassified — the call happened but could not be matched to any of your outcomes. false retries the contact, true retires it.
This is not a timeout. A call's outcome is authoritative and a contact is never advanced without one: if classification is slow, only that contact waits while the campaign keeps calling everyone else.
false
Per-contact dial cap. One of the two independent stop conditions — the other is the stop-disposition set.
1 <= x <= 9993
Separate budget for retries after a platform-side failure — a call that never reached the person. These do not consume max_attempts and are never terminal.
0 <= x <= 203
Delay before retrying a contact who did not answer.
30 <= x <= 6048003600
Delay before retrying a contact whose call completed but landed on a non-stop disposition.
60 <= x <= 60480086400
Which time of day a retry lands on. false keeps the wait exact, so a one-week wait retries at the same hour a week later. true picks a different hour inside the campaign's calling window, so repeat attempts do not always arrive at the same moment. The wait length itself is unchanged either way — a randomized retry is still never earlier than the configured wait.
false
Ring a second time shortly after an unanswered first ring.
false
Gap between the two rings when double_dial_enabled is true.
10 <= x <= 360090
Daily admission cap, counted against the workspace timezone day.
1 <= x <= 100000200
Minimum spacing between two calls handed to the queue by this campaign.
0 <= x <= 8640030
Calls this campaign may have live at once. Platform concurrency limits still apply on top — raising this does not buy the campaign extra capacity.
1 <= x <= 82
Hard spend cap for the campaign, enforced against spent_cents + reserved_cents (credits are debited when a call ends, so in-flight cost has to count). null means no cap. On hit the campaign moves to paused_budget.
x >= 1Your lawful basis for calling this list. Required before launch — recorded on the campaign's launch audit record alongside the enrolled count.
lawful_basis_confirmed is what the dashboard records: a single attestation that the caller has consent or another lawful basis for everyone on the list. The four specific values are for callers that know which one applies.
lawful_basis_confirmed, consent, existing_customer, non_marketing, registry_screened Do not admit contacts before this time.
Do not admit contacts after this time.
Engine-owned. Calls handed to the queue during the current day window.
Engine-owned. Day daily_admitted_count belongs to.
Engine-owned. When this campaign last handed a call to the queue.
Engine-owned. Projected campaign cost at the time of launch.
Engine-owned. Settled spend so far.
Engine-owned. Worst-case cost of in-flight calls, held against budget_cents until they settle.
Engine-owned. Last time the engine evaluated this campaign.
Engine-owned. Machine-readable answer to "why is nothing happening right now" — e.g. daily cap reached, outside the calling window, waiting on in-flight calls.
Engine-owned. Last error the engine recorded for this campaign.
Engine-owned. First launch time.
Engine-owned. When the last contact reached a terminal state.
Engine-owned. Contacts currently enrolled.
Engine-owned counter roll-up. Prefer GET /campaigns/{id}/stats.
Null when the campaign was created via the API.