Skip to main content
POST
Stop campaign
Ends a campaign for good. The status becomes stopped, and every contact that was still waiting to be called (pending or scheduled) is retired to excluded. stopped is terminal. A stopped campaign cannot be resumed, relaunched or edited — PATCH /campaigns/{id} returns 400 afterwards. Its history stays readable through GET /campaigns/{id}, GET /campaigns/{id}/leads and GET /campaigns/{id}/stats, which is the difference between stopping and archiving. Required scope: campaigns:manage.

Path parameters

There is no request body. Stop works from any non-terminal status, including draft.

What stop does

  • Retires waiting contacts (pending, scheduled) to excluded. They are never called again by this campaign.
  • Leaves contacts mid-flight (dialing, awaiting_disposition) alone. Those calls complete and get categorized normally, and their outcomes are recorded — you will see spent_cents rise slightly after a stop.
  • Does not cancel calls already handed to the dialer. Up to max_in_flight may still connect. If you need those cancelled too, use DELETE /campaigns/{id}.
  • Does not touch the do-not-call list, the underlying leads, or any contact already in a terminal state.
If you only want to stop calling for now, use POST /campaigns/{id}/pause — it is reversible and preserves every retry timer.

Example request

Example response

HTTP 200 with the full campaign object, same shape as GET /campaigns/{id}:
Trimmed for brevity — the real response contains every field listed on GET /campaigns/{id}. completed_at stays null: it marks a campaign that worked through its whole list, not one that was cut short. total_leads is unchanged — stopping excludes contacts rather than un-enrolling them, so GET /campaigns/{id}/stats still reports the full breakdown of what happened.

Errors

Authorizations

Authorization
string
header
required

Your Yappr API key (e.g. ypr_live_...). Generate one in the dashboard under Settings → API Keys.

Path Parameters

id
string<uuid>
required

Response

Campaign stopped

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.

id
string<uuid>
company_id
string<uuid>
name
string
Example:

"July reactivation"

description
string | null
status
enum<string>

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.

Available options:
draft,
scheduled,
running,
paused,
paused_insufficient_credit,
paused_budget,
paused_infra,
paused_config,
completed,
stopped,
archived
agent_id
string<uuid> | null

Agent that runs the calls. Required before launch.

agent
object | null

Full Agent object for agent_id.

from_phone_number_id
string<uuid> | null

Number the campaign calls from. Required before launch, and must still be active at launch time.

from_phone_number
object | null

Full PhoneNumber object for from_phone_number_id.

from_number
string | null

Engine-owned. E.164 snapshot of the from-number, kept for audit even if the number is later released.

Example:

"+972551234567"

retry_rules
object

Optional per-outcome retry overrides. Free-form object — the explicit max_attempts / retry_*_seconds fields cover the common cases.

calling_window
object

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.

stop_disposition_ids
string<uuid>[]

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.

stop_dispositions
object[]

Full Disposition objects for stop_disposition_ids.

stop_on_no_answer
boolean

Retire a contact the first time a call goes unanswered.

Example:

false

stop_on_voicemail
boolean

Retire a contact the first time a call reaches an answering machine.

Example:

false

stop_on_unclassified
boolean

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.

Example:

false

max_attempts
integer

Per-contact dial cap. One of the two independent stop conditions — the other is the stop-disposition set.

Required range: 1 <= x <= 999
Example:

3

max_infra_retries
integer

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.

Required range: 0 <= x <= 20
Example:

3

retry_no_answer_seconds
integer

Delay before retrying a contact who did not answer.

Required range: 30 <= x <= 604800
Example:

3600

retry_completed_seconds
integer

Delay before retrying a contact whose call completed but landed on a non-stop disposition.

Required range: 60 <= x <= 604800
Example:

86400

randomize_retry_time
boolean

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.

Example:

false

double_dial_enabled
boolean

Ring a second time shortly after an unanswered first ring.

Example:

false

double_dial_gap_seconds
integer

Gap between the two rings when double_dial_enabled is true.

Required range: 10 <= x <= 3600
Example:

90

max_calls_per_day
integer

Daily admission cap, counted against the workspace timezone day.

Required range: 1 <= x <= 100000
Example:

200

min_seconds_between_calls
integer

Minimum spacing between two calls handed to the queue by this campaign.

Required range: 0 <= x <= 86400
Example:

30

max_in_flight
integer

Calls this campaign may have live at once. Platform concurrency limits still apply on top — raising this does not buy the campaign extra capacity.

Required range: 1 <= x <= 8
Example:

2

budget_cents
integer | null

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.

Required range: x >= 1
regulatory_basis
enum<string> | null

Your 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.

Available options:
lawful_basis_confirmed,
consent,
existing_customer,
non_marketing,
registry_screened
starts_at
string<date-time> | null

Do not admit contacts before this time.

ends_at
string<date-time> | null

Do not admit contacts after this time.

daily_admitted_count
integer

Engine-owned. Calls handed to the queue during the current day window.

daily_window_date
string<date> | null

Engine-owned. Day daily_admitted_count belongs to.

last_admitted_at
string<date-time> | null

Engine-owned. When this campaign last handed a call to the queue.

estimate_cents
integer | null

Engine-owned. Projected campaign cost at the time of launch.

spent_cents
integer

Engine-owned. Settled spend so far.

reserved_cents
integer

Engine-owned. Worst-case cost of in-flight calls, held against budget_cents until they settle.

last_tick_at
string<date-time> | null

Engine-owned. Last time the engine evaluated this campaign.

last_tick_result
string | null

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.

last_error
string | null

Engine-owned. Last error the engine recorded for this campaign.

started_at
string<date-time> | null

Engine-owned. First launch time.

completed_at
string<date-time> | null

Engine-owned. When the last contact reached a terminal state.

total_leads
integer

Engine-owned. Contacts currently enrolled.

stats
object

Engine-owned counter roll-up. Prefer GET /campaigns/{id}/stats.

created_at
string<date-time>
updated_at
string<date-time>
created_by
string<uuid> | null

Null when the campaign was created via the API.