List Campaign Recipients
Retrieve a paginated list of a campaign's recipients — the people the campaign was actually sent to — with each recipient's send status and, on request, their delivery outcome, engagement, and purchases.
This is the per-recipient counterpart to the campaign-wide totals returned by
Get Campaign with ?include=stats,revenue. Each row is one
recipient, and the default response is lean: contact identity and send status
only. Request the delivery, engagement, and purchases sections through the
include parameter to add per-recipient detail.
Recipients vs. live audience
This endpoint returns the historical sent set: who the campaign was already sent to, and what happened to each of them. It is fixed once a campaign has been sent, and returns an empty list for a campaign that has not been sent yet.
That is different from List Campaign Contacts, which resolves a campaign's audience live and answers who a send right now would target. That list keeps changing as contacts are added, removed, unsubscribe, or become undeliverable; this one records who was mailed and never re-evaluates.
Send status is not inbox delivery
status reflects the send attempt, not inbox delivery. sent means the message
was accepted for delivery — it does not confirm the message reached the
inbox. Confirmed delivery is reported by delivery.delivered, available when you
request include=delivery.
Request
GET /campaigns/{uuid}/recipients
Path Parameters
| Parameter | Type | Description |
|---|---|---|
uuid | string | Campaign's UUID |
Query Parameters
All parameters are optional.
| Parameter | Type | Default | Description |
|---|---|---|---|
page | integer | 1 | Page number (min: 1) |
per_page | integer | 100 | Items per page (min: 1, max: 100) |
status | string | - | Filter by send status: pending, sent, or failed. |
search | string | - | Partial, case-insensitive match on the recipient's name or email address. |
opened | boolean | - | true returns only recipients who opened the email; false only those who didn't. Omit to include all. |
clicked | boolean | - | true returns only recipients who clicked a tracked link; false only those who didn't. Omit to include all. |
purchased | boolean | - | true returns only recipients with an attributed purchase; false only those without. Omit to include all. |
link | string | - | A tracked link's UUID. Returns only recipients who clicked that link. Get valid link UUIDs from List Campaign Link Clicks. A UUID that is not one of the campaign's tracked links returns 422 VALIDATION_ERROR. |
include | string | - | Comma-separated list of extra per-recipient sections to add to each row: delivery, engagement, purchases (include=delivery,engagement,purchases). Unrecognised values are ignored. |
Each section you name in include is added as an extra key on every row. A
section you don't request is omitted entirely — it is not returned as null — so
the default response stays lean. See Delivery Object,
Engagement Object, and Purchases Object.
An invalid status, opened, clicked, purchased, or link value — including
a link UUID that does not belong to the campaign — returns 422 VALIDATION_ERROR.
Example Requests
List a campaign's recipients:
curl -X GET "https://email.easy.tools/api/v1/campaigns/550e8400-e29b-41d4-a716-446655440000/recipients?page=1&per_page=50" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
Add per-recipient delivery, engagement, and purchases:
curl -X GET "https://email.easy.tools/api/v1/campaigns/550e8400-e29b-41d4-a716-446655440000/recipients?include=delivery,engagement,purchases" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
Find only the recipients who clicked a specific tracked link:
curl -X GET "https://email.easy.tools/api/v1/campaigns/550e8400-e29b-41d4-a716-446655440000/recipients?link=b25cdf8d-47d3-4d30-b097-e83a05577e8e" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Accept: application/json"
Response
Success Response (200 OK)
The default response carries identity and send status only:
{
"data": [
{
"contact_uuid": "6b505897-43c7-4e27-925d-dd67a6f0a80b",
"email": "john@example.com",
"first_name": "John",
"last_name": "Doe",
"status": "sent",
"sent_at": "2026-06-08T05:56:49Z",
"failed_at": null,
"created_at": "2026-06-08T05:56:49Z"
},
{
"contact_uuid": "e294fbd1-726c-4c98-afd8-d8e6908bcda7",
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Smith",
"status": "failed",
"sent_at": null,
"failed_at": "2026-06-08T05:56:51Z",
"created_at": "2026-06-08T05:56:49Z"
}
],
"pagination": {
"current_page": 1,
"per_page": 100,
"total": 2
}
}
A campaign with no recipients yet — for example one that has not been sent —
returns an empty data array.
Including delivery and engagement
With include=delivery,engagement, each row gains a delivery and an
engagement section:
{
"data": [
{
"contact_uuid": "e294fbd1-726c-4c98-afd8-d8e6908bcda7",
"email": "jane@example.com",
"first_name": "Jane",
"last_name": "Smith",
"status": "sent",
"sent_at": "2025-11-11T13:12:14Z",
"failed_at": null,
"created_at": "2025-11-11T13:12:14Z",
"delivery": {
"delivered": true,
"delivered_at": "2025-11-11T12:12:24Z",
"bounced": true,
"bounce_type": "soft",
"bounced_at": "2025-11-16T12:12:14Z",
"complained": true,
"complained_at": "2025-11-17T12:12:14Z"
},
"engagement": {
"opened": true,
"opened_at": "2025-11-11T14:12:14Z",
"open_count": 3,
"clicked": true,
"clicked_at": "2025-11-11T14:17:14Z",
"click_count": 2
}
}
],
"pagination": {
"current_page": 1,
"per_page": 100,
"total": 2
}
}
Including purchases
With include=purchases, each row gains a purchases section. revenue is the
per-currency total attributed to that recipient, and items are the individual
line items behind it:
{
"data": [
{
"contact_uuid": "6b505897-43c7-4e27-925d-dd67a6f0a80b",
"email": "john@example.com",
"first_name": "John",
"last_name": "Doe",
"status": "sent",
"sent_at": "2026-03-05T07:54:44Z",
"failed_at": null,
"created_at": "2026-03-05T07:54:44Z",
"purchases": {
"purchased": true,
"purchase_count": 2,
"revenue": [
{ "amount": 7032, "currency": "pln" }
],
"items": [
{
"order_uuid": "b86dad58-87da-420c-a6ec-38d2bc4c099b",
"product_uuid": "9354ecda-05bd-4144-bb8f-ba9c59fc8d61",
"product_name": "Pro plan",
"variant_name": null,
"is_subscription": false,
"amount": 3900,
"currency": "pln",
"purchased_at": "2026-03-05T07:10:41Z"
},
{
"order_uuid": "b86dad58-87da-420c-a6ec-38d2bc4c099b",
"product_uuid": "c2b1841a-73e6-4148-9784-28551af632b8",
"product_name": "Add-on",
"variant_name": "annual",
"is_subscription": false,
"amount": 3132,
"currency": "pln",
"purchased_at": "2026-03-05T07:10:41Z"
}
]
}
}
],
"pagination": {
"current_page": 1,
"per_page": 100,
"total": 2
}
}
Response Fields
Every row carries these fields. The delivery, engagement, and purchases
keys are present only when named in include.
| Field | Type | Description |
|---|---|---|
contact_uuid | string | The recipient contact's unique identifier |
email | string | The contact's current email address. May differ from the address originally mailed if the contact changed it after the send. |
first_name | string|null | Recipient's first name. null when not set. |
last_name | string|null | Recipient's last name. null when not set. |
status | string | Send status: pending, sent, or failed. See Send status is not inbox delivery. |
sent_at | string|null | ISO 8601 timestamp (UTC) the message was sent. null until sent. |
failed_at | string|null | ISO 8601 timestamp (UTC) the send failed. null unless status is failed. |
created_at | string | ISO 8601 timestamp (UTC) the recipient record was created. |
delivery | object | Delivery outcome. Only present when delivery is named in include. See Delivery Object. |
engagement | object | Opens and clicks. Only present when engagement is named in include. See Engagement Object. |
purchases | object | Attributed purchases. Only present when purchases is named in include. See Purchases Object. |
Delivery Object
Present only when delivery is named in include.
| Field | Type | Description |
|---|---|---|
delivered | boolean | Whether delivery to the inbox was confirmed. |
delivered_at | string|null | ISO 8601 timestamp (UTC) of confirmed delivery. null when not delivered. |
bounced | boolean | Whether the message bounced. |
bounce_type | string|null | hard, soft, or null when the message did not bounce. |
bounced_at | string|null | ISO 8601 timestamp (UTC) of the bounce. null when the message did not bounce. |
complained | boolean | Whether the recipient marked the message as spam. |
complained_at | string|null | ISO 8601 timestamp (UTC) of the complaint. null when there was none. |
Engagement Object
Present only when engagement is named in include.
| Field | Type | Description |
|---|---|---|
opened | boolean | Whether the recipient opened the email. |
opened_at | string|null | ISO 8601 timestamp (UTC) of the first open. null when never opened. |
open_count | integer | Total number of opens. |
clicked | boolean | Whether the recipient clicked any tracked link. |
clicked_at | string|null | ISO 8601 timestamp (UTC) of the first click. null when never clicked. |
click_count | integer | Total number of link clicks. |
Purchases Object
Present only when purchases is named in include.
| Field | Type | Description |
|---|---|---|
purchased | boolean | Whether any purchase is attributed to the recipient. |
purchase_count | integer | Total number of attributed purchases. |
revenue | array | Attributed revenue, one entry per currency. Empty array [] when there is none. See Revenue entry. |
items | array | The purchased line items, one entry per product per order. Empty array [] when there is none. See Purchase item. |
Revenue entry
| Field | Type | Description |
|---|---|---|
amount | integer | Revenue in the currency's minor units (for example cents or grosze). Divide by 100 for the major-unit value. |
currency | string | Lowercase ISO 4217 currency code, e.g. "usd" or "pln". |
Purchase item
| Field | Type | Description |
|---|---|---|
order_uuid | string | UUID of the order the item belongs to. Repeats across items from the same multi-product order — group by it to reconstruct orders. |
product_uuid | string | UUID of the purchased product. |
product_name | string | Product name. |
variant_name | string|null | Variant name, or null when the product has no variant. |
is_subscription | boolean | Whether the product is a subscription. |
amount | integer | Item price in the currency's minor units. Divide by 100 for the major-unit value. |
currency | string | Lowercase ISO 4217 currency code, e.g. "usd" or "pln". |
purchased_at | string | ISO 8601 timestamp (UTC) of the purchase. |
Pagination Object
| Field | Type | Description |
|---|---|---|
current_page | integer | Current page number |
per_page | integer | Number of items per page |
total | integer | Number of recipient rows matching the applied filters |
Unfiltered, total counts every send attempt — including pending and failed
ones — so it can be larger than the campaign's sent count and will not equal
stats.sends. For campaign-wide totals such as sends, opens, clicks, and rates,
use Get Campaign with ?include=stats,revenue.
Error Responses
Not Found Error (404)
{
"error": {
"code": "RESOURCE_NOT_FOUND",
"message": "Campaign with UUID '550e8400-e29b-41d4-a716-446655440000' not found"
}
}
Validation Error (422 Unprocessable Entity)
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The given data was invalid",
"details": {
"status": ["The selected status is invalid."]
}
}
}