Skip to main content

Campaigns

A campaign dials a list of contacts with one agent. You add contacts, start it, and Vocobase works through the list at the concurrency you set — retrying the ones that do not connect and reporting progress as it goes.

How it works

1

Create the campaign

Pick the agent and the caller IDs to dial from. The campaign starts in draft and nothing dials yet.
2

Add contacts

Up to 10,000 per request. Numbers are canonicalised, so the same number written two ways becomes one contact.
3

Start it

Vocobase dials up to concurrency_limit contacts at once — clamped by your account’s own concurrency limit — and retries failures on your schedule, inside the dialling window.
4

Watch it

Poll GET /campaigns/{id}/stats, or let the ordinary session.completed webhook deliver each call’s transcript and extraction as it finishes.

Create a campaign

The response is the campaign in draft, with a campaign_id you use for everything below.

Choosing caller IDs

There are two ways to say which number the campaign dials from, and they are mutually exclusive: Sending both fails unless they agree. Each number must be active, owned by you, and voice-capable — this is re-checked when the campaign starts, so a campaign whose numbers were deactivated while it sat in draft refuses to start rather than dispatching doomed calls.
tata_smartflo is rejected on campaigns. Smartflo does not support streaming voice bots on Broadcast/Dialer campaigns yet — only 1:1 click-to-call and inbound DIDs.

Retries

retry_count is how many times a contact is re-dialled after the first attempt. The wait between attempts comes from one of two fields:
  • cooldown_minutes — the same wait every time.
  • retry_delays_minutes — a per-attempt list, e.g. [30, 240] waits 30 minutes before the second attempt and 4 hours before the third. It is consulted per attempt: once the list runs out, later attempts fall back to cooldown_minutes. Max 20 entries, max 10080 minutes each.
retry_count accepts up to 20. retry_concurrency reserves slots for retries rather than capping them — it stops a backlog of retries from starving fresh contacts, and retries may still borrow the whole pool once fresh contacts are exhausted. It is inert at its default of 0.

Add contacts

Five things worth knowing:
  • metadata reaches the bot, but not as a template variable. It arrives under campaign_context.contact_metadata, not as a pre-call variable — writing {{plan}} in the prompt renders an empty string. Prompt variables come from the campaign’s column-to-variable map, which the v2 API does not expose, so variables is empty on API-created campaigns and metadata does not appear on the session.completed payload.
  • Contacts are reused, not duplicated. A number already in your contact library is linked rather than re-created — that is the split between contacts_created and contacts_existing.
  • duplicates means already on this campaign. Those rows are skipped, not re-queued.
  • Adding to a finished campaign re-opens it to paused, not running. A completed campaign moves back to paused and campaign_reopened is true. Dialling has not resumed — you must call POST /campaigns/{id}/resume yourself, which is what re-validates caller IDs and provider access before any new call goes out. This is how you top up a campaign instead of creating a new one.
  • A stopped campaign can never be topped up. /stop is terminal and is keyed on the stop itself, not the status, so a stopped campaign that later reads completed still returns 400 INVALID_STATE.

Run it

Each returns the updated campaign. Calling one from the wrong status returns 400 INVALID_STATE. Pausing lets in-flight calls finish and dispatches no new ones. Stopping is terminal — every still-queued contact is marked skipped and the campaign cannot be resumed.
start requires enough balance for at least one 30-second call. resume only re-checks the balance when the campaign is in credit_exhausted — resuming from paused runs no credit check. Both re-validate every caller ID. If the balance runs out mid-run the campaign moves to credit_exhausted on its own; top up and resume.
Both start and resume also accept an optional body to shift concurrency mid-campaign:
concurrency_limit is clamped to 1–10; retry_concurrency must be between 0 and the resulting concurrency_limit. Omit the body to keep the campaign’s current values.

The dialling window

Campaigns only dial between 09:00 and 20:59 in your account’s timezone. The window is fixed — there is no per-campaign override and no exemption, including for promised callbacks. A campaign started outside the window reaches running and simply dials nothing until 09:00. Retries are rolled forward into the window when they are booked, and held again at dispatch time, so cooldown_minutes and retry_delays_minutes are a minimum wait, never an exact one — a retry due at 22:00 goes out the next morning.

Track progress

The top five counters partition your contacts — they add up to total_contacts. dispositions does not: it breaks down only the calls that reached an outcome.
screener_encountered cuts across dispositions rather than partitioning them. A call can be both completed and screener-encountered, so do not add it to the disposition counts.

One contact’s history

GET /campaigns/{id}/contacts/{contactId}/journey returns every touch for a single contact, oldest first — each outbound attempt with its disposition and cost, plus any inbound call they made back. Use it to answer “what actually happened to this lead” without stitching sessions together yourself.

Callbacks

By default, calls ask the lead for a better time if they cannot talk now, and the campaign re-queues them for it. callbacks_pending in the stats is how many are waiting. Set callback_enabled: false at creation to turn this off. It is an opt-out, not an opt-in.

Campaign-wide extraction

Attach an extraction set to the campaign and its keys apply to every call the campaign makes, on top of whatever the agent already extracts:
Rejected with 409 EXTRACTION_KEY_CLASH if a key in the set collides with one the agent already produces — two sources writing the same field would silently shadow each other.

Next steps

Pre-call Variables

Use contact metadata inside the prompt.

Webhook Payloads

Receive each call’s transcript and result.

Credits & Billing

What a campaign costs to run.

API Reference

Every campaign field and error code.