curl --request POST \
--url https://api.goyappr.com/campaigns/{id}/pause \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.goyappr.com/campaigns/{id}/pause"
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}/pause', 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}/pause",
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}/pause"
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}/pause")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.goyappr.com/campaigns/{id}/pause")
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>"
}Pause a campaign
Moves the campaign to paused and stops admitting new calls; calls already
connected finish normally. Required scope campaigns:manage.
A manual pause is never lifted automatically — unlike
paused_insufficient_credit, it survives a top-up. Resume it yourself with
POST /campaigns/{id}/resume.
curl --request POST \
--url https://api.goyappr.com/campaigns/{id}/pause \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.goyappr.com/campaigns/{id}/pause"
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}/pause', 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}/pause",
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}/pause"
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}/pause")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.goyappr.com/campaigns/{id}/pause")
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>"
}paused. Contacts keep their state, attempt counts and retry timers, so resuming picks up exactly where the campaign left off.
A manual pause is deliberately sticky: it never auto-resumes. Topping up your balance, raising a budget or fixing a configuration problem will not restart a campaign you paused by hand — only POST /campaigns/{id}/resume (or POST /campaigns/{id}/launch) will.
Required scope: campaigns:manage.
Path parameters
| Parameter | Type | Notes |
|---|---|---|
id | uuid | Campaign ID. Must belong to the API key’s workspace. |
What pause does and does not do
- Does stop new calls immediately — the next evaluation skips the campaign.
- Does not cancel calls already on their way. Up to
max_in_flightcalls may still connect and complete, then settle normally with their outcomes recorded against the campaign. - Does not touch enrollment. Nothing is excluded, no attempt counters are reset.
- Does not reset
daily_admitted_count. Pausing and resuming inside the same day does not buy extra calls beyondmax_calls_per_day.
POST /campaigns/{id}/stop (terminal, retires waiting contacts) or DELETE /campaigns/{id} (archives and cancels queued calls).
Which statuses can be paused
running, scheduled, and any engine-applied pause — paused_insufficient_credit, paused_budget, paused_infra, paused_config.
Pausing an engine-paused campaign is a useful move rather than a no-op: it converts an auto-resuming pause into a manual one. A campaign sitting in paused_insufficient_credit would otherwise start dialing the moment funds arrive; pause it first and it waits for you.
draft, paused, completed, stopped and archived cannot be paused and return 400.
Example request
curl -X POST "https://api.goyappr.com/campaigns/CAMPAIGN_ID/pause" \
-H "Authorization: Bearer $YAPPR_API_KEY"
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": "paused",
"daily_admitted_count": 37,
"daily_window_date": "2026-07-28",
"last_admitted_at": "2026-07-28T11:58:04.010Z",
"spent_cents": 8140,
"reserved_cents": 1000,
"last_tick_at": "2026-07-28T11:59:00.412Z",
"last_tick_result": "spacing",
"started_at": "2026-07-27T06:00:11.900Z",
"completed_at": null,
"total_leads": 412,
"updated_at": "2026-07-28T12:00:44.118Z"
}
GET /campaigns/{id}.
reserved_cents stays non-zero until the in-flight calls settle, which is expected right after a pause.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | — | Campaign is draft, already paused, completed, stopped or archived. The message names the current status. |
| 401 | INSUFFICIENT_SCOPE | API key lacks campaigns:manage. |
| 404 | — | No campaign with that ID in this workspace, or it has been archived. |
Authorizations
Your Yappr API key (e.g. ypr_live_...). Generate one in the dashboard under Settings → API Keys.
Path Parameters
Response
Campaign paused
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.