Campaigns API
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 guide.
All requests require an Authorization: Bearer YOUR_GUAVA_API_KEY header. See the API Overview for details.
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}.
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.
Create a campaign
POST /v2/campaignsCreate 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. |
max_attempts | integer | No | Maximum call attempts per contact. Defaults to 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 |
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
curl -X POST https://api.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,
"max_concurrency": 2
}'Sample response:
{
"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,
"max_concurrency": 2,
"timezone": "America/Los_Angeles",
"description": null,
"created_datetime": "2026-05-20T18:04:00+00:00"
}List campaigns
GET /v2/campaignsList your organization's active (non-deleted) campaigns.
Response
A JSON array of campaign objects, each in the same shape returned by Get a campaign.
Errors
| Status | Description |
|---|---|
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
Example
curl -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
https://api.goguava.ai/v2/campaignsGet 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.
Errors
| Status | Description |
|---|---|
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
| 404 | No campaign with this code exists |
Example
curl -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
https://api.goguava.ai/v2/campaigns/gcmp-a1b2c3d4Update 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. |
max_attempts | integer | No | Maximum attempts per contact |
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
curl -X PATCH https://api.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.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
campaign_code | string | Yes | The gcmp- code of the campaign |
Response
{ "ok": true }Errors
| Status | Description |
|---|---|
| 401 | Invalid authentication |
| 403 | Outbound dialing not enabled for your organization |
| 404 | No campaign with this code exists |
Example
curl -X DELETE https://api.goguava.ai/v2/campaigns/gcmp-a1b2c3d4 \
-H 'Authorization: Bearer YOUR_GUAVA_API_KEY'Get campaign status
GET /v2/campaigns/{campaign_code}/statusRetrieve 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
curl -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
https://api.goguava.ai/v2/campaigns/gcmp-a1b2c3d4/statusSample response:
{
"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-contactsReport 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
curl -H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
https://api.goguava.ai/v2/campaigns/gcmp-a1b2c3d4/has-callable-contactsUpload contacts
POST /v2/campaigns/{campaign_code}/contactsAdd 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
curl -X POST 'https://api.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:
{ "created": 2 }List contacts
GET /v2/contactsList 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 |
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
curl -G https://api.goguava.ai/v2/contacts \
-H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
--data-urlencode 'campaign_code=gcmp-a1b2c3d4' \
--data-urlencode 'status=TRYING'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.
Questions? hi@goguava.ai
curl -X POST https://api.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,
"max_concurrency": 2
}'{
"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,
"max_concurrency": 2,
"timezone": "America/Los_Angeles",
"description": null,
"created_datetime": "2026-05-20T18:04:00+00:00"
}curl -X PATCH https://api.goguava.ai/v2/campaigns/gcmp-a1b2c3d4 \
-H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"enabled": false}'{
"campaign": "Spring outreach",
"status_counts": {
"TRYING": 120,
"COMPLETED": 55,
"PARTIALLY_COMPLETED": 3,
"FAILED": 22
}
}curl -X POST 'https://api.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"}
]
}'curl -G https://api.goguava.ai/v2/contacts \
-H 'Authorization: Bearer YOUR_GUAVA_API_KEY' \
--data-urlencode 'campaign_code=gcmp-a1b2c3d4' \
--data-urlencode 'status=TRYING'