---
title: 'Voice broadcast campaigns: outbound AI calls at scale'
description: >-
  Run outbound AI call campaigns with per-contact templates, calling windows,
  retries, voicemail detection, and a per-contact CSV report.
summary: >-
  Create a broadcast on a published AI agent with per-contact templates, load
  contacts, and start. The dialer keeps to calling windows, retries no-answers
  and voicemail, and reports every outcome.
date: 2026-07-21T00:00:00.000Z
type: use-case
---


## Summary

A voice broadcast dials a list of contacts and puts a live AI agent on every answered call. Not a recording blasted at a thousand phones — a conversation, personalised per contact, that can answer questions, take a decision, and write the outcome back to your systems. You define the campaign once: which [agent](/ai-agents) speaks, what it says to each contact, when it is allowed to dial, and how it retries the people it missed. [Broadcasts](/broadcasts) handle the rest — pacing, retries, voicemail detection, and a per-contact report at the end.

Payment reminders, collections follow-ups, appointment confirmations, outage notifications, renewal calls, event reminders: anything that today means a human working down a spreadsheet, or an SMS that never gets read.

## Who this is for

- Collections and credit teams calling hundreds of accounts a day with due-date reminders and payment follow-ups.
- Clinics, schools, and service businesses confirming appointments and sending time-sensitive notices people actually hear.
- Utilities, SACCOs, and fintechs pushing notifications that need acknowledgement, not just delivery.
- Ops teams who have outgrown manual call lists but do not want to be a robocall operation.

## How it works

A broadcast runs a published AI agent against a contact list. The agent brings the conversational ability — the voice, the tools, the judgement about when to end a call. The broadcast brings the campaign machinery: the schedule, the retry policy, the per-contact personalisation, and the report.

### 1. Create the campaign

`prompt_template` is composed on top of the agent's `base_prompt` for every call, with `{{variable}}` placeholders filled per contact. `start_phrase` is the exact opening line the agent speaks when the contact answers — script it, so every call starts identically and identifies you.

```js
const res = await fetch("https://api.sautikit.com/v1/broadcasts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SAUTIKIT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "July payment reminders",
    agent_id: process.env.REMINDER_AGENT_ID, // must be a published agent
    caller_number_id: process.env.CAMPAIGN_NUMBER_ID,
    prompt_template:
      "Remind {{name}} that their instalment of KES {{amount}} is due on {{due_date}}. If they can pay, confirm when. If not, capture a promise-to-pay date. Be brief and courteous.",
    start_phrase:
      "Hello, this is Wema Credit calling with a reminder about your account.",
    schedule_days: [1, 2, 3, 4, 5], // Mon-Fri
    daily_start_minute: 540, // 09:00
    daily_end_minute: 1020, // 17:00
    timezone: "Africa/Nairobi",
    max_attempts: 3,
    backoff_minutes: 120,
    retry_on: ["no_answer", "busy", "failed", "voicemail"],
  }),
});

const broadcast = await res.json();
```

The calling window matters as much as the script. The dialer will not place a call outside `daily_start_minute` to `daily_end_minute` in your `timezone`, or on days missing from `schedule_days` — a contact queued at 16:59 on Friday who cannot be reached rolls to Monday morning, not to Friday night.

### 2. Load contacts

Contacts carry the variables the template needs. Append them as JSON — one bad row is rejected individually and never sinks the batch — or upload a CSV in the dashboard for the same result.

```js
await fetch(
  `https://api.sautikit.com/v1/broadcasts/${broadcast.id}/contacts`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SAUTIKIT_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      contacts: [
        {
          phone: "+254712345678",
          variables: { name: "Alice", amount: "4,500", due_date: "Friday 25th" },
        },
        {
          phone: "+254700000001",
          variables: { name: "Ben", amount: "12,000", due_date: "Friday 25th" },
        },
      ],
    }),
  },
);
```

### 3. Start it — and stay in control

```js
await fetch(`https://api.sautikit.com/v1/broadcasts/${broadcast.id}/start`, {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.SAUTIKIT_API_KEY}` },
});
```

`/pause` stops dialing immediately and `/resume` picks the queue back up, so a wrong script caught after fifty calls stays a fifty-call mistake. `/duplicate` clones a finished campaign — same agent, template, and schedule — for next month's run.

## Retries and voicemail: reach people, don't spam machines

`max_attempts` and `backoff_minutes` govern the redial loop, and `retry_on` says which outcomes earn another attempt: `no_answer`, `busy`, `failed`, and `voicemail`. Voicemail handling is detection plus retry — when a machine answers, the dialer marks the attempt `voicemail` and schedules the next try inside your calling window. Your reminder reaches a person or it does not count as reached; it is never read out to an answering machine.

## Real conversations, not robocalls

Every answered call is the full agent experience. The contact can interrupt mid-sentence and be heard. They can ask questions — "which instalment is this?" — and the agent answers from the context the template gave it. And because agents carry HMAC-signed HTTP tools against your endpoints, the call can *do* things: check the live balance before quoting it, capture a promise-to-pay date directly into your collections system, or reschedule the appointment it was calling to confirm. The agent ends the call itself when the conversation is done, and every call is capped by the agent's `max_call_minutes`.

That difference shows up in the outcomes. A robocall delivers audio; an agent call comes back with a result per contact — paid, promised Thursday, disputed, asked not to be called again.

## Test with one call before you dial a thousand

`POST /v1/agents/{id}/send` places a single ad-hoc call through the same composition path — the agent's `base_prompt` plus your `prompt_template` and variables — without any campaign machinery. Call your own phone, hold the conversation, and tune the script before the list ever hears it.

```js
await fetch(`https://api.sautikit.com/v1/agents/${AGENT_ID}/send`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SAUTIKIT_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    to: "+254712345678", // your own number
    prompt_template:
      "Remind {{name}} that their instalment of KES {{amount}} is due on {{due_date}}.",
    variables: { name: "Alice", amount: "4,500", due_date: "Friday 25th" },
  }),
});
```

## Measure it

`GET /v1/broadcasts/{id}/report.csv` returns the campaign snapshot — queued, attempted, reached, and per-contact outcomes — ready for a spreadsheet or your BI pipeline. For any individual call, `GET /v1/calls/{id}?transcript=1` has the per-turn transcript with tool calls, so "what exactly did the agent promise Mrs. Otieno" is a lookup, not a guess. Calls are recorded by default.

> 
> 
> The hosted [Sautikit MCP server](/mcp) exposes `create_broadcast`, `start_broadcast`, `pause_broadcast`, and `get_broadcast_report`, so an AI assistant like Claude can build, launch, and review a campaign from a conversation.
> 
> 

## What it costs

Campaign calls bill like any other agent call: the standard per-second outbound call rate plus KES 4.00 per minute of AI time, metered per second. A 35-second confirmed reminder costs 35 seconds of each — unanswered attempts and voicemail detections do not burn AI minutes talking to nobody. Concurrency seats pace the dialer: the free tier runs 1 concurrent AI call, paid tiers 5, 8, or 10. The wallet is prepaid via M-Pesa, so a campaign can never spend more than the balance you loaded — a broadcast that hits an empty wallet pauses instead of overdrafting.

> 
> 
> Outbound campaigns carry consent and calling-hour obligations. Dial contacts who opted in, keep to lawful calling hours in each market, identify yourself in the `start_phrase`, and give people a way to opt out — the agent can capture "stop calling me" as an outcome like any other.
> 
> 

## Next steps

- [Broadcasts](/broadcasts): the product page — campaign features, pacing, and pricing.
- [AI agents](/ai-agents): the agents that make the calls, and what they can do on one.
- [Route inbound calls to an AI agent](/developers/guides/route-inbound-calls-to-an-ai-agent): the inbound side of the same agents.
- [Outbound lead qualification use case](/use-cases/outbound-lead-qualification): the build-your-own-model approach to outbound AI calling.
- [AI receptionist use case](/use-cases/ai-receptionist): put the same agents on your inbound line.
