Campaigns API
Create and run outbound campaigns — define lists (stages), enrol contacts, and manage the approval queue. Reads need campaigns.read, writes campaigns.write, and deciding an approval needs campaigns.approve.
List & manage campaigns
GET /api/v1/rest/campaigns ?status=
POST /api/v1/rest/campaigns
GET /api/v1/rest/campaigns/:id
PATCH /api/v1/rest/campaigns/:id
DELETE /api/v1/rest/campaigns/:idSet status to active to launch a campaign or paused to halt it. max_call_duration_seconds caps how long any outbound call may run. Campaigns run in one of two modes: "mode": "rules" (your stages drive everything) or "mode": "agentic" — give the agent an objectiveand it decides each contact's next touch, bounded by agentic_max_touches and (optionally) agentic_require_approval. Send-window and daily-cap fields (timezone, send_window_start/end, daily_cap_phone/sms/email) are settable on create and PATCH. service_booking_window_days sets a minimum lead time, in calendar days, for service appointments booked on the campaign — with 3, the agent cannot offer or write an appointment sooner than three days out, and slots inside the gap are removed from the availability it sees. service_booking_weekdays (PATCH) limits those appointments to certain weekdays, 0 = Sunday to 6 = Saturday — [1,2,3,4,5] books Monday to Friday only, even where the shop opens on Saturday; null allows every day. Trigger an immediate pass with POST /api/v1/rest/campaigns/:id/run, and inspect the schedule with GET /api/v1/rest/campaigns/:id/next-run.
Lists
GET /api/v1/rest/campaigns/:id/lists
POST /api/v1/rest/campaigns/:id/listsA list is a stage in the campaign. Wire it to an agent and a channel (phone, sms, email); with trigger_on_enter set, entering the list enqueues outbound automatically.
Contacts
GET /api/v1/rest/campaigns/:id/contacts ?list_id= &status=
POST /api/v1/rest/campaigns/:id/contacts enrol an EXISTING contact
POST /api/v1/rest/campaigns/:id/contacts/add create + enrol in one call
POST /api/v1/rest/campaigns/:id/contacts/bulk up to 1000 rows/contacts enrols a contact you already have — create it first via the Contacts API. Most integrations want /contacts/add instead, which takes a phone or email and creates the underlying contact if the workspace has no match, so pushing a new lead is one request./contacts/bulk takes the same shape as rows for an import.
Both /contacts/add and /contacts/bulk accept two fields worth knowing about:
{
"phone": "+16135550142",
"first_name": "Dale",
"facts": { "trade_in_vehicle": "2019 F-150", "source": "autoverify" },
"do_not_call": false
}facts is a free-form bag of per-contact attributes. The agent reads them on the call, and email and SMS templates merge them as {{facts.<key>}} — with a fallback as {{facts.trade_in_vehicle|your vehicle}}. This is how a campaign is personalised; a contact added without facts is one the agent knows nothing about.
do_not_call marks the contact suppressed, and it is the real suppression flag the call queue checks — not a fact. A fact named do_not_call suppresses nothing. It is set-only: true suppresses, and false never clears an existing suppression, so re-sending a stale list cannot un-suppress someone who opted out.
Approvals
GET /api/v1/rest/campaigns/:id/approvals ?status=pending
POST /api/v1/rest/campaigns/:id/approvalsFor lists that require sign-off, outbound messages wait in the approval queue. POST { approval_id, decision } with decision of approved or rejected — the decision is final.
Why a campaign did (or did not) do something
GET /api/v1/rest/campaigns/:id/events audit trail
GET /api/v1/rest/campaigns/:id/decisions agentic reasoning log
GET /api/v1/rest/campaigns/:id/next-run due now, next run, cap usage
POST /api/v1/rest/campaigns/:id/lists/:listId/trigger/events records every action a campaign took and every evaluation that decided to do nothing, which is what answers “why has this contact not advanced”. For agentic campaigns, /decisionsadds the planner’s own reasoning per tick — what it concluded about the lead and whether it chose to act, wait, or finish.
When nothing is going out, /next-run tells you whether the campaign is simply capped: it reports caps.phone/sms/email with cap, sent, remaining and capped for today, on the campaign’s own date. A cap of null means unlimited, not spent.
/lists/:listId/triggerforces a single stage’s schedule conditions to evaluate now and dispatches whatever they moved — narrower than /run, which evaluates the whole campaign.
Live outbound calls are covered by the Campaign calls API. The wider campaign surface — bulk contact import, CRM contact sources with auto-sync, write-back rules, webhook trigger tokens, templates, and AI campaign generation — is fully available too; every endpoint is in the OpenAPI reference.