Campaign lifecycle
Draft to finished, and every state it can be in on the way.
See Campaigns for the full field reference.
| # | Call | State after |
|---|---|---|
| 1 | POST /v1/campaigns | draft |
| 2 | POST /{id}/recipients — up to 500 per request | draft |
| 3 | POST /{id}/preflight | draft — nothing changes |
| 4 | POST /{id}/launch with Idempotency-Key | running |
| 5 | POST /{id}/pause / resume | paused / running |
| 6 | Last recipient settles, or POST /{id}/stop | completed |
Pre-flight is free and tells you everything
It returns the exclusion buckets — invalid_number, dnc, frequency_cap,
duplicate, dnd_scrub, blocked_by_gate — with each recipient counted against
exactly one reason, plus the compliance checks, an estimated cost band and your
balance. It changes nothing.
A block verdict makes launch a 422. A warn does not.
⚠️ duplicate always reads 0 here — duplicates are dropped when recipients are
added, so the real count is in the add-recipients response.
Launch is the point of no return
🔴 Idempotency-Key is required; without it, 400. Launching twice dials
the whole list twice, which is exactly the complaint pattern TCCCPR punishes.
Launch also refuses when:
- the agent is still a draft,
- the number routes to your own stack — refused at the button rather than as
thousands of per-call
422s, - the calling window plus the retry tail would fall outside the legal band,
- scrubbing fails. A scrubber outage fails the launch with a
503rather than passing an unscrubbed list. That is the one outcome TCCCPR forbids.
Launch snapshots the agent, the number and the purpose. The run finishes on the version it launched with.
While it runs
curl -H "Authorization: Bearer $TONE_KEY" "$TONE_API/v1/campaigns/$C/outcomes"
curl -H "Authorization: Bearer $TONE_KEY" "$TONE_API/v1/calls?campaign_id=$C"The recipient list names each recipient's last attempt. For every call
including retries, filter the call log by campaign_id.
It can pause itself: below autoPauseBelowPaise it stops with
pausedReason: "low_balance" and emits campaign.paused. Top up, then resume.
Changing a running campaign
You cannot. agentId can never change, and everything else freezes once the
campaign leaves draft. Pause, publish your prompt change, and relaunch — or
POST /{id}/duplicate, which copies the campaign and its recipients into a fresh
draft.
After it finishes
POST /{id}/recipients/{recipientId}/redial queues one settled recipient for
another attempt, and a completed campaign wakes up for it. POST /{id}/archive
hides it from the list without deleting anything.