List blasts
GET /
Returns your campaigns' email blasts with their main stats, newest first.
Scope: blasts:read
Blasts are ordered by created_at (then id), newest first, and rows are never repeated or skipped. The first page fixes the window: a blast created while you page does not join the walk; it heads the next list you start without a cursor.
List rows carry the main stats. opened, clicked and the day-by-day breakdown are only on GET /v1/blasts/{blast_id}.
Without campaign_id the list covers every campaign whose blasts the key can read; a campaign_id must be one of them, otherwise 404.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
campaign_id | UUID | No | Only this campaign. |
status | string | No | Only blasts with this status. One of draft, scheduled, sending, paused, completed, cancelled, failed. |
since | timestamp | No | Only blasts created at or after this time. ISO 8601; a time without an offset is read as UTC (URL-encode + as %2B). |
limit | integer | No | Blasts per page. Every blast's stats are counted on each request, so pages are small. 1 to 20. Default: 20. |
cursor | string | No | The next_cursor of the previous page, unchanged. It carries the filters: send it alone, or with exactly the filters of the first page. Up to 4,096 characters. |
Response
Status: 200 OK
| Name | Type | Description |
|---|---|---|
data | array of objects | Blasts, newest first. |
data. | UUID | The blast. |
data. | string | Its name in DialEcho. |
data. | UUID | The blast's campaign. |
data. | string | The blast status. One of draft, scheduled, sending, paused, completed, cancelled, failed. |
data. | timestamp | When it was created (RFC 3339, UTC). |
data. | timestamp | null | When it is or was scheduled to start; null when it was not scheduled. |
data. | string | null | The subject as written, merge tags included. Each send's subject is the subject its recipient got. |
data. | object | The main stats. |
data. | integer | Every recipient of the blast, in any status. |
data. | integer | Delivered sends: status delivered, plus sent through any sender but SendGrid, whose sends count once SendGrid confirms delivery. |
data. | integer | Sends that bounced. |
data. | integer | Sends that failed. |
data. | integer | Recipients not emailed because the address is suppressed. |
data. | integer | Recipients not sent yet (pending or processing). Cancelled and skipped recipients count only in recipients_total. |
data. | integer | Delivered recipients who replied: a reply DialEcho matched to their send. |
data. | integer | Recipients who opted out: their matched reply was an opt-out, or the provider reported an unsubscribe. Unsubscribe-link clicks cannot be tied to a blast and are not counted. |
data. | number | null | bounced / (delivered + bounced), rounded to 4 decimals; null while both are 0. |
data. | number | null | replied / delivered, rounded to 4 decimals and never above 1; null while nothing was delivered. |
next_cursor | string | null | Pass it back as cursor for the next page; null when there is none. |
has_more | boolean | true while next_cursor is set. |
Example request
curl
bash
curl "https://development.agencydevworks.ai/v1/blasts?campaign_id=cccccccc-0000-4000-8000-000000000001&limit=20" \
-H "Authorization: Bearer dei_your_key"JavaScript
js
const response = await fetch("https://development.agencydevworks.ai/v1/blasts?campaign_id=cccccccc-0000-4000-8000-000000000001&limit=20", {
headers: {
"Authorization": "Bearer dei_your_key",
},
});
const data = await response.json();Example response
Status: 200 OK
json
{
"data": [
{
"id": "22222222-0000-4000-8000-000000000001",
"name": "Fall re-engagement",
"campaign_id": "cccccccc-0000-4000-8000-000000000001",
"status": "completed",
"created_at": "2026-11-02T14:00:00Z",
"scheduled_at": "2026-11-03T15:00:00Z",
"subject_template": "{{first_name}}, your solar quote is ready",
"stats": {
"recipients_total": 1200,
"delivered": 1100,
"bounced": 30,
"failed": 5,
"suppressed": 40,
"pending": 20,
"replied": 25,
"opt_outs": 4,
"bounce_rate": 0.0265,
"reply_rate": 0.0227
}
}
],
"next_cursor": null,
"has_more": false
}Errors
| HTTP | Code | When |
|---|---|---|
| 403 | insufficient_scope | The key does not have blasts:read. Checked before the query is validated. |
| 404 | not_found | campaign_id is not a campaign whose blasts the key can read. |
| 409 | resync_required | The cursor expired (after 30 days) or the key's or its member's access changed since it was issued. Start again without a cursor. |
| 422 | invalid_cursor | The cursor was not issued to this key for this list, was changed, or was sent with different filters. |
| 422 | invalid_request | A parameter is invalid. error.fields names the first invalid filter (campaign_id, status, since), or query.limit (over 20) or query.cursor. |
| 503 | dependency_unavailable | Blast data or the access check is temporarily unavailable. Retry with backoff. |
Every endpoint can also answer 401 invalid_api_key, 429 rate_limited and 503 dependency_unavailable. See Errors in the overview.
Related endpoints
- GET
/· Retrieve a blastv1/ blasts/ {blast_id} - GET
/· List a blast's sendsv1/ blasts/ {blast_id}/ sends