# Retrieve a blast

`GET /v1/blasts/{blast_id}`

Returns one blast with its full stats and a day-by-day breakdown.

**Scope:** `blasts:read`

The same blast as the list returns, with `opened` and `clicked` added to `stats` and a `daily` breakdown. Blasts store no start or finish time, so none is returned; `daily` shows when sends, bounces and replies happened.

`daily` lists only UTC days with activity, oldest first. Sends are dated by `sent_at`, bounces by when the bounce was recorded and replies by each delivered recipient's first reply.

## Path parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `blast_id` | `UUID` | Yes | The blast. |

## Response

**Status:** `200 OK`

| Name | Type | Description |
| --- | --- | --- |
| `id` | `UUID` | The blast. |
| `name` | `string` | Its name in DialEcho. |
| `campaign_id` | `UUID` | The blast's campaign. |
| `status` | `string` | The blast status. One of `draft`, `scheduled`, `sending`, `paused`, `completed`, `cancelled`, `failed`. |
| `created_at` | `timestamp` | When it was created (RFC 3339, UTC). |
| `scheduled_at` | `timestamp \| null` | When it is or was scheduled to start; `null` when it was not scheduled. |
| `subject_template` | `string \| null` | The subject as written, merge tags included. Each send's `subject` is the subject its recipient got. |
| `stats` | `object` | The main stats plus `opened` and `clicked`. |
| `stats.recipients_total` | `integer` | Every recipient of the blast, in any status. |
| `stats.delivered` | `integer` | Delivered sends: status `delivered`, plus `sent` through any sender but SendGrid, whose sends count once SendGrid confirms delivery. |
| `stats.bounced` | `integer` | Sends that bounced. |
| `stats.failed` | `integer` | Sends that failed. |
| `stats.suppressed` | `integer` | Recipients not emailed because the address is suppressed. |
| `stats.pending` | `integer` | Recipients not sent yet (`pending` or `processing`). Cancelled and skipped recipients count only in `recipients_total`. |
| `stats.replied` | `integer` | Delivered recipients who replied: a reply DialEcho matched to their send. |
| `stats.opt_outs` | `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. |
| `stats.bounce_rate` | `number \| null` | `bounced / (delivered + bounced)`, rounded to 4 decimals; `null` while both are 0. |
| `stats.reply_rate` | `number \| null` | `replied / delivered`, rounded to 4 decimals and never above 1; `null` while nothing was delivered. |
| `stats.opened` | `integer \| null` | Delivered or sent recipients who opened it; `null` when the blast did not track opens. |
| `stats.clicked` | `integer` | Delivered or sent recipients who clicked a link. |
| `daily` | `array of objects` | One row per UTC day with activity, oldest first. |
| `daily.date` | `string` | The UTC day, `YYYY-MM-DD`. |
| `daily.sends` | `integer` | Sends that went out that day. |
| `daily.bounces` | `integer` | Bounces recorded that day. |
| `daily.replies` | `integer` | Delivered recipients whose first reply came that day. |

## Example request

**curl**

```bash
curl "https://development.agencydevworks.ai/v1/blasts/22222222-0000-4000-8000-000000000001" \
  -H "Authorization: Bearer dei_your_key"
```

**JavaScript**

```js
const response = await fetch("https://development.agencydevworks.ai/v1/blasts/22222222-0000-4000-8000-000000000001", {
  headers: {
    "Authorization": "Bearer dei_your_key",
  },
});
const data = await response.json();
```

## Example response

**Status:** `200 OK`

```json
{
  "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,
    "opened": null,
    "clicked": 44
  },
  "daily": [
    { "date": "2026-11-03", "sends": 600, "bounces": 12, "replies": 9 },
    { "date": "2026-11-04", "sends": 535, "bounces": 18, "replies": 16 }
  ]
}
```

## Errors

| HTTP | Code | When |
| --- | --- | --- |
| 403 | `insufficient_scope` | The key does not have `blasts:read`. Checked before the query is validated. |
| 404 | `not_found` | The blast does not exist or the key cannot read it: another organization, a campaign outside the key, or one where the key's member cannot use email blasts. |
| 422 | `invalid_request` | `blast_id` is not a UUID. |
| 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](/api/#errors) in the overview.

## Related endpoints

- `GET /v1/blasts` · [List blasts](/api/blasts/list)
- `GET /v1/blasts/{blast_id}/sends` · [List a blast's sends](/api/blasts/list-sends)
