---
title: Set Up a Predictive Dialer Campaign
description: >-
  Create a predictive dialer campaign with the SautiKit API, set the dial ratio
  and abandon target, handle waiting callers and tune the pacing.
summary: >-
  A predictive campaign dials a few more contacts than there are free agents,
  learns how often your list answers, and slows itself down when callers would
  wait.
date: 2026-10-28T00:00:00.000Z
type: guide
---


## Summary

A predictive dialer places more calls than there are free agents, because not every contact picks up. If your list answers one call in four, dialing exactly one number per free agent leaves agents listening to ringing most of the time. Dialing ahead keeps them talking.

SautiKit learns your list's answer rate from the campaign's own calls, hour by hour, and never has more than `ratio_max` calls out per free agent. When more people answer than there are agents, the extra caller hears a short hold line and is then sent to the next free agent, an AI agent or a callback offer. If that happens too often, the campaign lowers its own ratio.

Predictive dialing pays off for teams of about five agents or more working large, cold lists. For small teams, or lists where every answered caller must reach a person at once, use [progressive dialing](/developers/guides/run-a-progressive-dialer-campaign).

Every field, error and response is in the [API reference](/developers/api#campaigns).

## Prerequisites

- Everything a [progressive campaign](/developers/guides/run-a-progressive-dialer-campaign#prerequisites) needs: an API key with `dialer.write` and `dialer.read`, numbers to call from, an extension per agent and a funded wallet.
- A plan that includes predictive dialing (SautiDesk Pro). Without it, create and start answer `402 plan.required`.
- Optional: a published [AI agent](/developers/guides/route-inbound-calls-to-an-ai-agent) to talk to callers when every agent is busy.

---

## Steps

### Step 1: Create the campaign with `mode: predictive`

[Create a dialer campaign](/developers/api/createDialerCampaign) with `mode` set to `predictive`. Two settings control the pacing:

- `ratio_max`: the most calls out per free agent, 1.0 to 3.0, default 1.5. With 4 free agents at 1.5, no more than 6 calls are ever out, so at most 2 answered callers can wait. At 1.0 the campaign behaves like progressive dialing.
- `abandon_target_pct`: the share of answered callers who may wait more than 2 seconds for an agent, default 3. The campaign slows down whenever it runs above this.

For a new list, start with a `ratio_max` of 1.2 and raise it once you see the real answer rate.

**JavaScript**

```javascript
const BASE = "https://api.sautikit.com";
const headers = {
  Authorization: `Bearer ${process.env.SAUTIKIT_API_KEY}`,
  "Content-Type": "application/json",
};

const campaign = await fetch(`${BASE}/v1/dialer/campaigns`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    name: "Q4 loan offers",
    mode: "predictive",
    caller_ids: ["+254709000001", "+254709000002", "+254709000003"],
    ratio_max: 1.2,
    abandon_target_pct: 3,
    budget_minor: 2500000,
    daily_budget_minor: 500000,
    hours: {
      timezone: "Africa/Nairobi",
      windows: [{ days: [1, 2, 3, 4, 5], start_minute: 540, end_minute: 1080 }],
    },
    retry: { max_attempts: 4, backoff_minutes: [60, 240, 1440], retry_on: ["no_answer", "busy"], ordering: "best_time" },
    amd: { mode: "detect_and_act", on_machine: "hangup", max_analysis_seconds: 4 },
    overflow: {
      max_hold_ms: 2000,
      hold_line_text: "Thanks for picking up. Someone will be with you in a moment.",
      auto_ai: true,
      ai_agent_id: process.env.SAUTIKIT_AI_AGENT_ID,
      auto_ai_after_seconds: 8,
      callback_enabled: true,
    },
  }),
}).then((r) => r.json());

console.log(campaign.id, campaign.mode, campaign.ratio_max); // predictive 1.2
```

**Python**

```python
import os, requests

BASE = "https://api.sautikit.com"
headers = {"Authorization": f"Bearer {os.environ['SAUTIKIT_API_KEY']}"}

campaign = requests.post(
    f"{BASE}/v1/dialer/campaigns",
    headers=headers,
    json={
        "name": "Q4 loan offers",
        "mode": "predictive",
        "caller_ids": ["+254709000001", "+254709000002", "+254709000003"],
        "ratio_max": 1.2,
        "abandon_target_pct": 3,
        "budget_minor": 2500000,
        "daily_budget_minor": 500000,
        "hours": {
            "timezone": "Africa/Nairobi",
            "windows": [{"days": [1, 2, 3, 4, 5], "start_minute": 540, "end_minute": 1080}],
        },
        "retry": {"max_attempts": 4, "backoff_minutes": [60, 240, 1440], "retry_on": ["no_answer", "busy"], "ordering": "best_time"},
        "amd": {"mode": "detect_and_act", "on_machine": "hangup", "max_analysis_seconds": 4},
        "overflow": {
            "max_hold_ms": 2000,
            "hold_line_text": "Thanks for picking up. Someone will be with you in a moment.",
            "auto_ai": True,
            "ai_agent_id": os.environ["SAUTIKIT_AI_AGENT_ID"],
            "auto_ai_after_seconds": 8,
            "callback_enabled": True,
        },
    },
).json()

print(campaign["id"], campaign["mode"], campaign["ratio_max"])  # predictive 1.2
```

`ordering: "best_time"` dials the contacts most likely to answer at the current local hour first, and moves retries for hard-to-reach contacts to a better hour. Answering-machine detection matters more here than in any other mode: a machine that reaches an agent wastes the very time predictive dialing is meant to save.

### Step 2: Decide what a waiting caller hears

When more people answer than there are free agents, the extra caller is never left in silence. The `overflow` settings set what they hear, in order:

1. A brief silent hold (`max_hold_ms`). Keep it at 2,000 ms or less, so a caller whose agent frees up quickly is never counted as abandoned.
2. One spoken hold line (`hold_line_text` or a recording at `hold_line_url`).
3. An AI agent (`auto_ai` with `ai_agent_id`), after `auto_ai_after_seconds`. AI minutes are billed as usual.
4. A callback offer (`callback_enabled`): the caller presses 1 and is called back after `callback_delay_minutes`. Set `callback_mode: "ai"` to have an AI receptionist agree a time instead.
5. A short apology, then the call ends.

A supervisor can also send one waiting caller to an AI agent by hand with [hand off to AI](/developers/api/handoffDialerCallToAI).

### Step 3: Add contacts and join agents

These work exactly as in a progressive campaign: [add contacts](/developers/api/appendDialerContacts) with an `Idempotency-Key`, then [join each agent's extension](/developers/api/joinDialerCampaign), mark it [ready](/developers/api/readyDialerSession) and send a [heartbeat](/developers/api/heartbeatDialerSession) every 30 seconds. The [progressive guide](/developers/guides/run-a-progressive-dialer-campaign#step-3-join-your-agents) has the code.

With no ready agent, a predictive campaign dials nothing.

### Step 4: Check the price, then start

```bash
curl -s "https://api.sautikit.com/v1/dialer/campaigns/$ID/estimate" \
  -H "Authorization: Bearer $SAUTIKIT_API_KEY"

curl -s -X POST "https://api.sautikit.com/v1/dialer/campaigns/$ID/start" \
  -H "Authorization: Bearer $SAUTIKIT_API_KEY"
```

[Starting](/developers/api/startDialerCampaign) spends money. Your budget, daily budget and the wallet balance are hard stops: when one runs out, no more calls are placed.

### Step 5: Read the live numbers and tune

The [live board](/developers/api/getDialerLive) and the `dialer.stats` frame on the [event stream](/developers/api/streamDialerEvents) show how the pacing is doing:

| Field | What it tells you |
|---|---|
| `ratio` | Calls out per free agent right now. Below `ratio_max` means the campaign has slowed itself down. |
| `answer_rate` | People who answered over the last 100 dials. A low rate (0.1 to 0.2) means a cold list or a poor hour, and the campaign dials further ahead. |
| `abandon_rate_pct` | The share of answered callers who waited more than 2 seconds for an agent. Compare it with `abandon_target_pct`. |

When the abandon rate runs above the target, the ratio drops by 10% and SautiKit sends a `dialer.campaign.ratio_changed` [webhook](/developers/api/createWebhook). While the rate stays under half the target, the ratio creeps back up 2% at a time, never above `ratio_max`.

How to tune:

- **Many `ratio_changed` events, callers waiting:** lower `ratio_max` to 1.2 or 1.3, add agents, or turn on `overflow.auto_ai`.
- **Agents idle, abandon rate well under target:** raise `ratio_max` toward 2.0 and watch `abandon_rate_pct`.
- **One to three agents:** dialing ahead gains little, because the ratio rounds down per agent. Switch to progressive.

To change a setting, [pause](/developers/api/pauseDialerCampaign), [update](/developers/api/updateDialerCampaign) and [resume](/developers/api/resumeDialerCampaign). The [campaign report](/developers/api/getDialerCampaignReport) gives the same figures for the whole run, with `human_answer_rate`, `abandoned` and the cost per connected call.

---

## FAQ

### What is a predictive dialer?

A predictive dialer is an outbound calling system that places more calls than there are free agents, using the expected answer rate to predict how many calls will be picked up. Agents spend their time talking instead of waiting through ringing and unanswered calls.

### What is a good abandon rate for a predictive campaign?

SautiKit defaults to 3%, meaning three answered callers in a hundred wait more than 2 seconds for an agent. Treat it as a tuning setting. Some markets regulate abandoned calls, so check the rules where your contacts live before you run predictive campaigns there.

### How does SautiKit decide how many calls to place?

For each free agent it estimates how many dials it takes to get one answer, from the campaign's own results in that hour of the day, and dials that many. It never has more than `ratio_max` calls out per free agent, and it lowers the ratio when too many callers wait.

### What does a caller hear if every agent is busy?

A brief hold, a spoken line, and then whatever you set in `overflow`: an AI agent, a callback offer or a short apology. They never hear silence.

### Can I switch between predictive and progressive during a campaign?

Yes. Pause the campaign, [update](/developers/api/updateDialerCampaign) `mode` to `progressive` or `predictive`, and resume. Contacts, attempts and agent sessions carry over.

---

## Next steps

- [Set up a progressive dialer campaign](/developers/guides/run-a-progressive-dialer-campaign): one call per free agent, no waiting callers.
- [Route calls to an AI agent](/developers/guides/route-inbound-calls-to-an-ai-agent): build the agent that takes overflow callers.
- [Webhooks](/developers/concepts/webhooks): delivery, retries and the event list.
