Campaign¶
Campaign entity. A campaign is a scheduled send of a set of payloads (Email, SMS).
The <campaign> in the URL is the numerical ID of the campaign.
Endpoints¶
| Method | URL |
|---|---|
GET |
/campaign/<campaign> |
POST |
/campaign |
PUT |
/campaign/<campaign> |
DELETE |
/campaign/<campaign> |
GET /campaign/<campaign>¶
Payload: No payload.
Returns: The campaign.
Example:
curl -u "username:api_key" -H "Accept: application/json" \
https://app.duo.pt/campaign/{campaign_id}
Response:
{
"success": true,
"data": {
"campaign": {
"id": 1,
"owner": 1,
"list": 1,
"name": "Summer Newsletter 2026",
"domain": 1,
"segments": [],
"recipients": null,
"state": "DRAFT",
"when": {
"date": "2026-06-22 21:54:05.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"event_callback": null
},
"email": null,
"sms": null,
"statistics": {
"delivered": 0,
"bounced": 0,
"opened": 0,
"clicked": 0,
"unsubscribed": 0,
"emails_sent": 0,
"sms_sent": 0,
"sent": 0
}
}
}
The statistics object reports totals for the campaign:
| Field | Description |
|---|---|
emails_sent |
Email messages sent by this campaign |
sms_sent |
SMS messages sent by this campaign |
sent |
Total messages sent (emails_sent + sms_sent) |
delivered |
Messages confirmed delivered |
bounced |
Messages that bounced |
opened |
Unique opens (email only) |
clicked |
Unique clicks (email only) |
unsubscribed |
Unsubscriptions attributed to this campaign |
Tip
emails_sent and sms_sent give per-campaign, per-channel volume directly — no need to
page through the event collections to count messages.
POST /campaign¶
| Parameter | Type | Description |
|---|---|---|
name |
string | Campaign name |
domain |
int | Bounce domain ID (a Domain object enabled as bouncing domain) |
list |
int | ID of the contact list targeted by the campaign |
segments[] |
int array | IDs of segments within the list. May occur more than once. If none, campaign sends to all contacts. |
state |
string | Campaign state. Valid states: DRAFT⮂SCHEDULED⮂READY⇒SENDING⇒(DONE\|ERROR) |
when |
timestamp | Scheduled send time. Unix timestamp (int) or ISO 8601 string. |
event_callback |
string | Optional. URL called back when events occur. |
Note
Transitioning out of DRAFT requires the domain to have DNS validated (DKIM, SPF, bounce). The API accepts the PUT without error but leaves the state unchanged if the domain is not fully configured.
Returns: The new campaign.
Example:
curl -u "username:api_key" -H "Accept: application/json" \
-d "name=My+Campaign&domain={domain_id}&list={list_id}&state=DRAFT" \
https://app.duo.pt/campaign
Response:
{
"success": true,
"data": {
"list": {
"id": 1,
"owner": 1,
"name": "Example...",
"default_email_out": null,
"unsubscribe_message": "You have been unsubscribed.",
"email_out_custom_fields": null,
"double_optin": false,
"optout_allowed": false,
"optout_category": null,
"optin_email": null,
"optin_sms": null,
"shared_with_children": false,
"read_shared_with_children": false,
"segments": null
},
"campaign": {
"id": 1,
"owner": 1,
"list": 1,
"name": "Summer Newsletter 2026",
"domain": 1,
"segments": null,
"recipients": null,
"state": "DRAFT",
"when": "2026-06-22T21:54:05+00:00",
"event_callback": null
}
}
}
PUT /campaign/<campaign>¶
| Parameter | Type | Description |
|---|---|---|
name |
string | Campaign name |
domain |
int | Bounce domain ID (a Domain object enabled as bouncing domain) |
segments[] |
int array | IDs of segments within the list to target. May occur more than once. If none, campaign sends to all contacts. |
state |
string | Campaign state |
when |
timestamp | Scheduled send time |
event_callback |
string | Optional. URL called back when events occur. |
Returns: The edited campaign.
Example:
curl -u "username:api_key" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"name": "Summer Newsletter 2026", "domain": 1, "list": 1, "state": "SCHEDULED", "when": 1750000000}' \
https://app.duo.pt/campaign/{campaign_id}
Response:
{
"success": true,
"data": {
"campaign": {
"id": 1,
"owner": 1,
"list": 1,
"name": "Summer Newsletter 2026",
"domain": 1,
"segments": [],
"recipients": null,
"state": "DRAFT",
"when": {
"date": "2026-06-22 21:54:05.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"event_callback": null
}
}
}
DELETE /campaign/<campaign>¶
Payload: No payload.
Returns: The deleted campaign.
Example:
curl -u "username:api_key" -H "Accept: application/json" -X DELETE \
https://app.duo.pt/campaign/{campaign_id}
Response:
{
"success": true,
"data": {
"campaign": {
"id": 1,
"owner": 1,
"list": 1,
"name": "Summer Newsletter 2026",
"domain": 1,
"segments": [],
"recipients": null,
"state": "DRAFT",
"when": {
"date": "2026-06-22 21:54:05.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"event_callback": null
}
}
}
Callback Behaviour¶
If an event_callback URL is defined, a POST request is executed against it whenever a
message lifecycle event occurs.
Events¶
| Event | Description |
|---|---|
submitted |
Message submitted for delivery |
delivered |
Message delivered |
bounced |
Permanent delivery failure |
opened |
Message opened |
clicked |
A link in the message was clicked |
Note
SMS payloads only generate delivered and bounced events. Email payloads generate all events.
The event_callback URL may contain a query string — those parameters are forwarded unchanged.
Callback POST parameters¶
| Parameter | Type | Description |
|---|---|---|
event |
string | Which event occurred |
when |
int | Unix timestamp of the event |
telephone[] |
string array | Phone numbers for SMS events (telephone[0], telephone[1], ...) |
email[] |
string array | Emails for email events (email[0], email[1], ...) |
Note
Events are time-aggregated. A delay of up to five minutes is expected between the actual event and the callback execution.