> ## Documentation Index
> Fetch the complete documentation index at: https://docs.goguava.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Campaigns API

> Create and manage outbound campaigns and their contacts over HTTP

These endpoints let you create and manage outbound calling campaigns, upload contacts, and track progress. For a step-by-step walkthrough, see the [Outbound Campaigns](/guides/campaign) guide.

<Note>
  All requests require an `Authorization: Bearer YOUR_GUAVA_API_KEY` header. See the [API Overview](/api-reference/api-overview) for details.
</Note>

<Note>
  Campaigns are addressed by their **campaign code** — a `gcmp-` prefixed identifier (e.g. `gcmp-a1b2c3d4`) returned when you create a campaign. Use it wherever an endpoint takes a `{campaign_code}`.
</Note>

<Note>
  Every endpoint below is served under `/v2` and is addressed by campaign code. The older `/v1` paths — which addressed campaigns by their internal database `id` — still respond for backwards compatibility but are **deprecated**; switch to `/v2`.
</Note>

## Create a campaign

```
POST /v2/campaigns
```

Create a new outbound campaign. Campaign names must be unique within your organization.

### Parameters

| Name | Type | Required | Description |
| - | - | - | - |
| `name` | string | Yes | Human-readable campaign name, unique within your organization. Max 200 characters. |
| `origin_phone_numbers` | array | Yes | Phone numbers to place calls from, E.164 format (e.g. `+15551234567`). Must be numbers your account owns. |
| `start_date` | string | Yes | Date the campaign may begin dialing (`YYYY-MM-DD`). |
| `calling_windows` | array | Yes | Time windows during which calls may be placed. See [Calling windows](#calling-windows). |
| `max_attempts` | integer | No | Maximum call attempts per contact. Defaults to `1`. |
| `retry_delay_s` | integer | No | Seconds to wait before reattempting a contact. Defaults to `3600` (one hour), which is also the minimum. Maximum `2592000` (30 days). Has no effect unless `max_attempts` is above `1`. |
| `max_concurrency` | integer | No | Maximum simultaneous calls. Defaults to `1`. Cannot exceed your organization-wide limit. |
| `timezone` | string | No | IANA timezone used to interpret calling windows. Defaults to `"America/Los_Angeles"`. |
| `end_date` | string | No | Date after which no new calls are placed (`YYYY-MM-DD`). |
| `description` | string | No | Free-form description of the campaign. |

#### Calling windows

Each entry in `calling_windows` is an object:

| Name | Type | Required | Description |
| - | - | - | - |
| `day` | string or integer | Yes | Day of week — a name (`"monday"`…`"sunday"`) or an integer (`0` = Monday … `6` = Sunday). |
| `start_time` | string | Yes | Window start, 24-hour `HH:MM` (also accepts `9am`, `9:00AM`, etc.). |
| `end_time` | string | Yes | Window end, `HH:MM`. |

The recommended calling window is 8:00AM–9:00PM. Windows outside that range are accepted but the response includes a `warning`.

### Response

`201 Created` with the campaign object:

| Field | Type | Description |
| - | - | - |
| `id` | string | Internal identifier. Prefer `campaign_code` for all requests. |
| `campaign_code` | string | Public `gcmp-` identifier used to address this campaign. |
| `name` | string | Campaign name |
| `origin_phone_numbers` | array | Normalized E.164 origin numbers |
| `start_date` | string | `YYYY-MM-DD` |
| `end_date` | string (nullable) | `YYYY-MM-DD`, if set |
| `calling_windows` | array | The campaign's calling windows |
| `max_attempts` | integer | Maximum attempts per contact |
| `retry_delay_s` | integer | Seconds waited before reattempting a contact |
| `max_concurrency` | integer | Maximum simultaneous calls |
| `timezone` | string | IANA timezone |
| `description` | string (nullable) | Campaign description, if set |
| `created_datetime` | string | ISO 8601 creation time |
| `warning` | string | Present only if a calling window falls outside the recommended range |

### Errors

| Status | Description |
| - | - |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization, or an origin number is not owned by your account |
| 400 | Invalid origin phone number, or `max_concurrency` exceeds your organization limit |
| 409 | A campaign with this name already exists |
| 422 | A required field (`origin_phone_numbers`, `start_date`, `calling_windows`) is missing |

### Example

```bash theme={null}
curl -X POST https://app.goguava.ai/v2/campaigns \
  -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Spring outreach",
    "origin_phone_numbers": ["+15551234567"],
    "start_date": "2026-06-01",
    "calling_windows": [
      {"day": "monday", "start_time": "09:00", "end_time": "17:00"}
    ],
    "max_attempts": 3,
    "retry_delay_s": 14400,
    "max_concurrency": 2
  }'
```

Sample response:

```json theme={null}
{
  "id": "6064ab9663dc4eb0f1a2b3c4",
  "campaign_code": "gcmp-a1b2c3d4",
  "name": "Spring outreach",
  "origin_phone_numbers": ["+15551234567"],
  "start_date": "2026-06-01",
  "end_date": null,
  "calling_windows": [
    {"day": 0, "start_time": "09:00", "end_time": "17:00"}
  ],
  "max_attempts": 3,
  "retry_delay_s": 14400,
  "max_concurrency": 2,
  "timezone": "America/Los_Angeles",
  "description": null,
  "created_datetime": "2026-05-20T18:04:00+00:00"
}
```

***

## List campaigns

```
GET /v2/campaigns
```

List your organization's active (non-deleted) campaigns.

### Response

A JSON array of campaign objects, each in the same shape returned by [Get a campaign](#get-a-campaign).

### Errors

| Status | Description |
| - | - |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |

### Example

```bash theme={null}
curl -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
  https://app.goguava.ai/v2/campaigns
```

***

## Get a campaign

```
GET /v2/campaigns/{campaign_code}
```

Retrieve a single campaign by its campaign code.

### Parameters

| Name | Type | Required | Description |
| - | - | - | - |
| `campaign_code` | string | Yes | The `gcmp-` code of the campaign |

### Response

The campaign object, in the same shape returned by [Create a campaign](#create-a-campaign).

### Errors

| Status | Description |
| - | - |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
| 404 | No campaign with this code exists |

### Example

```bash theme={null}
curl -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
  https://app.goguava.ai/v2/campaigns/gcmp-a1b2c3d4
```

***

## Update a campaign

```
PATCH /v2/campaigns/{campaign_code}
```

Update one or more fields of an existing campaign. Only the fields you include are changed. Changing schedule-affecting fields (`calling_windows`, `timezone`, `start_date`, `end_date`) or `origin_phone_numbers` reschedules the campaign's pending contacts.

### Parameters

| Name | Type | Required | Description |
| - | - | - | - |
| `campaign_code` | string | Yes | The `gcmp-` code of the campaign |
| `enabled` | boolean | No | Pause (`false`) or resume (`true`) the campaign |
| `name` | string | No | New campaign name |
| `origin_phone_numbers` | array | No | Replacement origin numbers (E.164, must be owned by your account) |
| `start_date` | string | No | `YYYY-MM-DD` |
| `end_date` | string | No | `YYYY-MM-DD` |
| `calling_windows` | array | No | Replacement calling windows. See [Calling windows](#calling-windows). |
| `max_attempts` | integer | No | Maximum attempts per contact |
| `retry_delay_s` | integer | No | Seconds to wait before reattempting a contact (minimum `3600`). Applies to the next retry; contacts already waiting are not rescheduled. |
| `max_concurrency` | integer | No | Maximum simultaneous calls (cannot exceed your organization limit) |
| `timezone` | string | No | IANA timezone |
| `description` | string | No | Campaign description |

### Response

The updated campaign object. Includes a `warning` field if a supplied calling window is outside the recommended range.

### Errors

| Status | Description |
| - | - |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled, or an origin number is not owned by your account |
| 400 | No fields supplied, invalid origin phone number, or `max_concurrency` exceeds your organization limit |
| 404 | No campaign with this code exists |

### Example

```bash theme={null}
curl -X PATCH https://app.goguava.ai/v2/campaigns/gcmp-a1b2c3d4 \
  -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"enabled": false}'
```

***

## Delete a campaign

```
DELETE /v2/campaigns/{campaign_code}
```

Soft-delete a campaign. Deleted campaigns no longer place calls and stop appearing in [List campaigns](#list-campaigns).

### Parameters

| Name | Type | Required | Description |
| - | - | - | - |
| `campaign_code` | string | Yes | The `gcmp-` code of the campaign |

### Response

```json theme={null}
{ "ok": true }
```

### Errors

| Status | Description |
| - | - |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
| 404 | No campaign with this code exists |

### Example

```bash theme={null}
curl -X DELETE https://app.goguava.ai/v2/campaigns/gcmp-a1b2c3d4 \
  -H 'Authorization: Bearer YOUR_GUAVA_API_KEY'
```

***

## Get campaign status

```
GET /v2/campaigns/{campaign_code}/status
```

Retrieve a breakdown of contact statuses for a campaign — how many contacts are still being tried, completed, failed, etc.

### Parameters

| Name | Type | Required | Description |
| - | - | - | - |
| `campaign_code` | string | Yes | The `gcmp-` code of the campaign |

### Response

| Field | Type | Description |
| - | - | - |
| `campaign` | string | The campaign's name |
| `status_counts` | object | Map of contact status → count. Statuses: `TRYING`, `COMPLETED`, `PARTIALLY_COMPLETED`, `FAILED`. |

### Errors

| Status | Description |
| - | - |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
| 404 | No campaign with this code exists |

### Example

```bash theme={null}
curl -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
  https://app.goguava.ai/v2/campaigns/gcmp-a1b2c3d4/status
```

Sample response:

```json theme={null}
{
  "campaign": "Spring outreach",
  "status_counts": {
    "TRYING": 120,
    "COMPLETED": 55,
    "PARTIALLY_COMPLETED": 3,
    "FAILED": 22
  }
}
```

***

## Check for callable contacts

```
GET /v2/campaigns/{campaign_code}/has-callable-contacts
```

Report whether the campaign currently has any contacts eligible to be called. Useful for deciding whether to keep a worker running.

### Parameters

| Name | Type | Required | Description |
| - | - | - | - |
| `campaign_code` | string | Yes | The `gcmp-` code of the campaign |

### Response

| Field | Type | Description |
| - | - | - |
| `has_callable_contacts` | boolean | `true` if at least one contact is eligible to be called |

### Errors

| Status | Description |
| - | - |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
| 404 | No campaign with this code exists |

### Example

```bash theme={null}
curl -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
  https://app.goguava.ai/v2/campaigns/gcmp-a1b2c3d4/has-callable-contacts
```

***

## Upload contacts

```
POST /v2/campaigns/{campaign_code}/contacts
```

Add contacts to a campaign. Phone numbers are validated and normalized to E.164, checked against your organization's Do Not Call list, and de-duplicated within the campaign. Up to 10,000 contacts may be uploaded per request. If any contact is rejected, the entire request is rejected.

### Parameters

| Name | Type | Required | Description |
| - | - | - | - |
| `campaign_code` | string | Yes | The `gcmp-` code of the campaign (path) |
| `accepted_terms_of_service` | boolean | Yes | Must be `true`. Pass as a query parameter. |
| `allow_duplicates` | boolean | No | If `true`, skip the in-campaign duplicate check. Query parameter, defaults to `false`. |
| `contacts` | array | Yes | The contacts to add (request body). See below. |

Each contact object:

| Name | Type | Required | Description |
| - | - | - | - |
| `phone_number` | string | Yes | Contact's phone number (E.164 preferred). |
| `data` | object | No | Arbitrary key/value data merged into the agent's context for this contact. Max 16 KB serialized. |
| `outreach_modalities` | array | No | Agentic-tenacity modalities to use for this contact, e.g. `["sms"]`. |

### Response

`200 OK` on success:

| Field | Type | Description |
| - | - | - |
| `created` | integer | Number of contacts added |

If any contact is rejected, `400` is returned and **no** contacts are created:

| Field | Type | Description |
| - | - | - |
| `created` | integer | Always `0` |
| `rejected` | integer | Number of rejected contacts |
| `rejected_entries` | array | Per-contact `{ phone_number, reason }` objects |

### Errors

| Status | Description |
| - | - |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
| 400 | `accepted_terms_of_service` not passed, or one or more contacts were rejected |
| 404 | No campaign with this code exists |

### Example

```bash theme={null}
curl -X POST 'https://app.goguava.ai/v2/campaigns/gcmp-a1b2c3d4/contacts?accepted_terms_of_service=true' \
  -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "contacts": [
      {"phone_number": "+15551230001", "data": {"first_name": "Alex"}},
      {"phone_number": "+15551230002"}
    ]
  }'
```

Sample response:

```json theme={null}
{ "created": 2 }
```

***

## List contacts

```
GET /v2/contacts
```

List contacts across your campaigns, newest first. Filter by campaign, status, contact data, or attempt time.

### Parameters

| Name | Type | Required | Description |
| - | - | - | - |
| `campaign_code` | string | No | Only return contacts in this campaign (`gcmp-` code). |
| `status` | string | No | Only return contacts with this status. Repeat the parameter to match several. One of `TRYING`, `COMPLETED`, `PARTIALLY_COMPLETED`, `FAILED`. |
| `data` | string | No | JSON object of exact-match filters on contact `data`, e.g. `{"first_name":"Alex"}`. |
| `attempted_after` | string | No | Only contacts last attempted at or after this time (ISO 8601). |
| `attempted_before` | string | No | Only contacts last attempted at or before this time (ISO 8601). |
| `limit` | integer | No | Maximum contacts to return, 1–1000. Defaults to 100. |
| `after` | string | No | Pagination cursor. Pass the `next_cursor` from the previous response. |

### Response

| Field | Type | Description |
| - | - | - |
| `contacts` | array | The matching contacts. Each includes `id` (used by [Delete a contact](#delete-a-contact)), `phone_number`, `status`, and `data`. |
| `next_cursor` | string (nullable) | Pass as `after` to fetch the next page. `null` when there are no more results. |
| `has_more` | boolean | `true` if more contacts match than were returned |

### Errors

| Status | Description |
| - | - |
| 400 | Invalid `limit`, `after` cursor, `status`, `data`, or date value |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
| 404 | No campaign with the supplied code exists |

### Example

```bash theme={null}
curl -G https://app.goguava.ai/v2/contacts \
  -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
  --data-urlencode 'campaign_code=gcmp-a1b2c3d4' \
  --data-urlencode 'status=TRYING'
```

<Note>
  The deprecated `/v1/contacts` endpoint takes a `campaign_id` query parameter — your campaign's internal database `id` — in place of `campaign_code`. It still works for backwards compatibility, but new integrations should use `/v2/contacts` with `campaign_code`.
</Note>

***

## Delete contacts by phone number

```
DELETE /v2/campaigns/{campaign_code}/contacts
```

Permanently remove every contact in a campaign with the given phone number. If the number was uploaded more than once with `allow_duplicates`, all copies are removed. Contacts in other campaigns are not affected.

A contact that is being dialed or is mid-call can't be deleted. If any matching contact is, the request fails with `409` and **no** contacts are removed. Retry once the call has ended. (If a duplicate starts dialing while the request is running, the other copies may already be gone, but the one being dialed is kept and you still get the `409`.)

### Parameters

| Name | Type | Required | Description |
| - | - | - | - |
| `campaign_code` | string | Yes | The `gcmp-` code of the campaign (path) |
| `phone_number` | string | Yes | The contact's phone number. Query parameter. US numbers only, with or without the `+1`, e.g. `+12125551234`, `2125551234`, or `(212) 555-1234`. It is normalized to E.164 before matching. |

### Response

| Field | Type | Description |
| - | - | - |
| `deleted` | integer | Number of contacts removed |

### Errors

| Status | Description |
| - | - |
| 400 | `phone_number` is not a valid, dialable US phone number |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
| 404 | No campaign with this code exists, or no contact in it has this phone number |
| 409 | A matching contact is being dialed or is mid-call |
| 422 | `phone_number` is missing |

### Example

```bash theme={null}
curl -G -X DELETE https://app.goguava.ai/v2/campaigns/gcmp-a1b2c3d4/contacts \
  -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
  --data-urlencode 'phone_number=+15551230001'
```

Sample response:

```json theme={null}
{ "deleted": 1 }
```

***

## Delete a contact

```
DELETE /v2/campaigns/{campaign_code}/contacts/{contact_id}
```

Permanently remove a single contact from a campaign. Get the contact's `id` from [List contacts](#list-contacts).

A contact that is being dialed or is mid-call can't be deleted. Retry once the call has ended.

### Parameters

| Name | Type | Required | Description |
| - | - | - | - |
| `campaign_code` | string | Yes | The `gcmp-` code of the campaign the contact belongs to (path) |
| `contact_id` | string | Yes | The contact's `id` (path) |

### Response

| Field | Type | Description |
| - | - | - |
| `deleted` | integer | Number of contacts removed. Always `1` on success. |

### Errors

| Status | Description |
| - | - |
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
| 404 | No campaign with this code exists, or no contact with this `id` exists in it |
| 409 | The contact is being dialed or is mid-call |

### Example

```bash theme={null}
curl -X DELETE https://app.goguava.ai/v2/campaigns/gcmp-a1b2c3d4/contacts/6abc480bd5d63109d34d8914 \
  -H 'Authorization: Bearer YOUR_GUAVA_API_KEY'
```

Sample response:

```json theme={null}
{ "deleted": 1 }
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.