# Create a Campaign

> POST /v1/campaigns - create a voice broadcast or agent-dialer campaign with caller ID, TTS or audio message, concurrency, retries, calling window and schedule, then upload contacts and start it.

Source: https://edesy.in/docs/voice-api/campaigns/create-a-campaign

`POST https://voice-api.edesy.in/v1/campaigns`

Create a campaign with a `name`, a `from_number` you own and either a message (`audio_type` of `audio` or `tts`) for broadcast, or a `queue_id` for an agent dialer. Then upload contacts, optionally test and estimate it, and call `POST /v1/campaigns/{id}/start`. The portal's four-step campaign wizard does the same.

## 1. Create

```bash
curl -X POST https://voice-api.edesy.in/v1/campaigns \
  -H "Authorization: Bearer $EDESY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Diwali offer",
    "from_number": "+9180XXXXXXXX",
    "fallback_from_number": "+9180YYYYYYYY",
    "audio_type": "tts",
    "tts_text": "Namaste! Acme Diwali sale is live. Visit our store this week for 20 percent off.",
    "tts_language": "hi-IN",
    "max_concurrent": 10,
    "retry_attempts": 2,
    "retry_delay_sec": 1800,
    "ring_timeout_sec": 30,
    "amd_enabled": true,
    "recording_enabled": false,
    "window_start_minute": 600,
    "window_end_minute": 1200,
    "timezone": "Asia/Kolkata"
  }'
```

| Field | Type | Required | Description |
|---|---|---|---|
| `name` | string | yes | Campaign name |
| `from_number` | string | yes | Caller ID; a number in your workspace |
| `fallback_from_number` | string | no | Backup caller ID used while the main one's carrier is refusing calls. Omit to use the workspace default; `""` means none |
| `audio_type` | string | broadcast | `audio` (upload a file) or `tts` |
| `tts_text`, `tts_voice`, `tts_language` | string | for `tts` | Message text, voice and language |
| `max_concurrent` | integer | no | Simultaneous calls |
| `retry_attempts`, `retry_delay_sec` | integer | no | Retries for unanswered contacts, and the gap between them |
| `ring_timeout_sec` | integer | no | Seconds to ring each contact |
| `amd_enabled` | boolean | no | Answering-machine detection |
| `recording_enabled` | boolean | no | Record calls |
| `scheduled_at` | RFC 3339 | no | Start time |
| `window_start_minute`, `window_end_minute` | integer | no | Daily calling window in minutes after midnight. Default 540 to 1260 (09:00 to 21:00) |
| `timezone` | string | no | Timezone for the window, for example `Asia/Kolkata` |
| `queue_id` | UUID | agent dialer | Contact-center queue whose free agents take answered calls |
| `dial_ratio` | number | no | Agent dialer lines per free agent: `1.0` (progressive, default), `1.5` or `2.0` (power) |

The response is the campaign object in `data` with its `id` and `status`.

## 2. Add the message (broadcast with `audio`)

```bash
curl -X POST https://voice-api.edesy.in/v1/campaigns/{id}/audio \
  -H "Authorization: Bearer $EDESY_KEY" \
  -F "audio=@offer.mp3"
```

WAV or MP3.

## 3. Add contacts

CSV (multipart field `contacts`):

```bash
curl -X POST https://voice-api.edesy.in/v1/campaigns/{id}/contacts \
  -H "Authorization: Bearer $EDESY_KEY" \
  -F "contacts=@contacts.csv"
```

```
phone_number,name,consent_source,consent_timestamp
+9198XXXXXXXX,Asha,website_form,2026-10-01T10:00:00Z
+9197XXXXXXXX,Ravi,,
```

The phone column may be named `phone_number`, `phone`, `number`, `mobile` or `msisdn`; the name column `name` or `contact_name`. `consent_source` and `consent_timestamp` are optional and kept for your compliance export.

Or JSON:

```json
{ "contacts": [ { "phone_number": "+9198XXXXXXXX", "name": "Asha" } ] }
```

The response accounts for every row:

```json
{
  "data": {
    "contacts_added": 1,
    "total_rows": 2,
    "suppressed_dnc": 1,
    "suppressed_duplicate": 0,
    "invalid": 0,
    "invalid_samples": [],
    "consent_timestamp_unparsed": 0
  }
}
```

## 4. Test, estimate, start

```bash
# Hear the message on your own phone
curl -X POST https://voice-api.edesy.in/v1/campaigns/{id}/test-call \
  -H "Authorization: Bearer $EDESY_KEY" -H "Content-Type: application/json" \
  -d '{ "phone_number": "+9198XXXXXXXX" }'

# Cost and duration estimate
curl https://voice-api.edesy.in/v1/campaigns/{id}/estimate -H "Authorization: Bearer $EDESY_KEY"

# Start now
curl -X POST https://voice-api.edesy.in/v1/campaigns/{id}/start -H "Authorization: Bearer $EDESY_KEY"
```

To start later, `POST /v1/campaigns/{id}/schedule` with `scheduled_at` (and optionally a new window, or `"clear_window": true` to remove it). `DELETE /v1/campaigns/{id}/schedule` cancels the schedule.

## Control

| Action | Endpoint |
|---|---|
| Pause / resume | `POST /v1/campaigns/{id}/pause`, `/resume` |
| Stop for good | `POST /v1/campaigns/{id}/stop` |
| Run again | `POST /v1/campaigns/{id}/duplicate` with `{"audience": "all"}`, `"unreached"` or `"none"` |

See also [AMD, Fallback Caller ID and DNC](https://edesy.in/docs/voice-api/campaigns/amd-fallback-caller-id-and-dnc) and [Estimate and Results](https://edesy.in/docs/voice-api/campaigns/estimate-and-results).
