---
title: "Outbound campaigns"
description: "Upload a contact list and let an agent work through it — with calling windows, retries, and live progress."
---

Campaigns dial a list of contacts on your behalf: pick an agent, upload a
CSV, set when calling is allowed, and ThunderPhone works through the list
— retrying no-answers and voicemails on the schedule you choose. Find
them under **Engagement → Campaigns** (`/dashboard/campaigns`).

For one-off programmatic calls, use the
[outbound calls API](/guides/place-outbound-calls) instead.

## Prerequisites

- A deployed voice agent.
- An outbound-enabled phone number. Customer VoIP numbers continue to support
  campaigns. ThunderPhone numbers start inbound-only; an organization admin can
  request outbound calling from the Phone Numbers page. ThunderPhone reviews
  each request manually, and approved numbers can run campaigns. See
  [Connect a VoIP provider](/guides/voip-providers).
- Contacts as a CSV file (or pasted JSON) with a `phone` or
  `phone_number` column. **Up to 5,000 rows per upload**, UTF-8 encoded.
  New JSON uploads must be flat: extra values may be strings, numbers,
  booleans, or `null`. Rows containing arrays or nested objects are rejected
  individually with a validation message; valid rows still import. CSV cells
  are strings. Accepted extra columns are kept as per-contact variables on the campaign's
  contact records and automatically supplied to each dial. ThunderPhone also
  classifies the saved agent prompt before each outbound call to decide whether
  the agent should navigate a phone menu. Headers such as `name` and `account_id`
  match `{{name}}` and `{{account_id}}` in the saved agent's prompt. Name mapping can also combine columns into `name`. See
  [per-call variables](/guides/call-variables) for defaults, naming rules,
  the 32 KB object limit, and values returned in completion webhooks.

Older contact records may contain nested metadata from earlier JSON uploads.
That metadata stays on the stored contact. Each dial omits non-scalar values from
its variables payload and logs the ignored key names for that attempt, without
logging values. Retries use the same filtering.

## Create a campaign

<Steps>
  <Step title="Click New campaign">
    Name it, pick the **voice agent**, and choose the **from number**
    (one of your outbound-enabled numbers).
  </Step>
  <Step title="Upload the contacts CSV">
    Rows with an invalid phone number or invalid variables are rejected individually and
    reported back with their row numbers — the rest import fine.
  </Step>
  <Step title="Set the calling window">
    Choose a **timezone** and a **daily start/end** window; the campaign
    only dials inside it. The timezone starts on your from number's zone
    (its area code in the US and Canada, its country elsewhere) or, for
    numbers that span several zones such as toll-free ones, your
    browser's. The hint under the field says which. The window defaults
    to 9am–5pm. A window that starts before 8am or ends after 9pm shows a
    warning: calls outside those hours may break calling-hours rules such
    as the TCPA's. An end earlier than the start crosses midnight: 22:00 →
    02:00 dials from 10pm until 2am the next day, with a warning. The
    window runs in the campaign's timezone, not each contact's, so split
    contacts by timezone when they are spread out.
    Optionally set a **start at** to schedule the launch and an
    **end at** after which the campaign completes automatically.
  </Step>
  <Step title="Tune pacing and retries">
    - **Concurrent calls** — how many calls this campaign runs at once
      (1–10, default 3). Campaigns start no new call while your
      organization has 10 outbound calls in progress, so parallel
      campaigns share that budget. Every call also counts toward your
      organization's [concurrent-call limit](/api-reference/introduction#call-limits)
      (20 by default, across inbound, outbound, web and test calls).
      When that limit is reached, or the same number was called in the
      last 10 seconds, the contact simply waits for a later pass; its
      attempts are not used up.
    - **Maximum attempts** — how many times each contact may be dialed
      (1–10, default 2).
    - **Retry backoff (minutes)** — the wait before a retry (at least 30,
      default 60).
    - **Retry on** — which outcomes trigger a retry: **no answer**,
      **voicemail**, **failed** (all on by default).

    Need more attempts or shorter spacing? Contact support: staff can
    raise both limits for your organization.
  </Step>
  <Step title="Create draft, then start">
    Campaigns are created as drafts — contacts can only be added while a
    campaign is a draft. Starting a campaign requires an explicit consent
    to the call charges. With a future **start at** it becomes
    *scheduled*; otherwise it starts *running* immediately.
  </Step>
</Steps>

<Note>
  Campaigns saved before these limits keep running exactly as configured.
  Their attempts and spacing stay as saved through other edits.
</Note>

## How outcomes and retries work

After each call, the contact gets an outcome — successful calls are
marked completed; a call graded as no-answer, detected as voicemail, or
failed is retried if that outcome is in your **retry on** list and the
contact has attempts left. Contacts that run out of attempts are marked
*exhausted*; non-retryable failures are marked *failed*.

<Note>
  Dialing pauses automatically while your account balance isn't in good
  standing, and resumes once it is. Keep
  [auto top-up](/guides/billing-and-topups) on for long campaigns.
</Note>

## Monitor and control

Each campaign card shows its status and progress (e.g. "173 of 248
finished"). Open a campaign for the status breakdown, per-contact
results, and recent calls — each links to the full transcript and
recording in call history.

- **Pause / resume** — pausing stops new dials; calls already in
  progress finish naturally. Paused campaigns can be edited and resumed.
- **Cancel** — permanently stops the campaign.
- A campaign completes on its own when every contact reaches a final
  state or the **end at** time passes.

Campaign statuses: `draft`, `scheduled`, `running`, `paused`,
`completed`, `cancelled`. Contact statuses: `pending`, `scheduled`,
`calling`, `completed`, `failed`, `exhausted`.

## Next steps

<CardGroup cols={2}>
  <Card title="Watch calls live" icon="tower-broadcast" href="/guides/monitor-live-calls">
    Listen in on campaign calls as they happen.
  </Card>
  <Card title="Outbound calls API" icon="phone-arrow-up-right" href="/guides/place-outbound-calls">
    Place individual calls programmatically.
  </Card>
</CardGroup>
