# Broadcast API

> Create, schedule, send, and manage one-time email broadcasts through the Broadcast REST API: full endpoint reference, parameters, and authenticated examples.

Source: https://sendbroadcast.net/docs/api-broadcasts

The Broadcast API provides full CRUD (Create, Read, Update, Delete) operations for managing email broadcast campaigns. You can create, list, view, update, delete, and send broadcasts programmatically.

## Required Permissions

All endpoints require authentication via an API token with appropriate permissions:

- **Read Permission**: Required for GET endpoints (list, show)
- **Write Permission**: Required for POST, PATCH, DELETE endpoints and send operations

## Broadcast Object

The broadcast object contains the following fields:

| Field | Description |
| --- | --- |
| `id` | The unique identifier of the broadcast |
| `subject` | The subject line of the email |
| `preheader` | Preview text that appears in email clients |
| `body` | The content of the email |
| `name` | The internal name of the broadcast |
| `track_opens` | Whether open tracking is enabled for this broadcast |
| `track_clicks` | Whether click tracking is enabled for this broadcast |
| `status` | Current status of the broadcast (draft, scheduled, sending, sent, etc.) |
| `html_body` | Whether the body contains HTML content |
| `html_body_add_footer` | Add the channel footer to an HTML email. An email with its own unsubscribe link (a footer block that shows the link, or a `{{ unsubscribe_url }}` link that every subscriber sees) never gets the channel footer on top, whatever this value (2.46.0 or later). Without an own link, rich text and block emails always get the channel footer, and HTML emails follow this value. |
| `content_type` | `rich_text`, `html` or `blocks` (the [Drag-and-Drop Email Builder](https://sendbroadcast.net/docs/block-email-builder)) |
| `blocks_json` | The block document when `content_type` is `blocks`; `body` holds the rendered HTML |
| `reply_to` | Reply-to email address |
| `sender_identity_id` | ID of the sender identity (From address) to send as; omit to use the channel default |
| `total_recipients` | Total number of recipients |
| `sent_at` | Timestamp when the broadcast was sent |
| `scheduled_send_at` | Timestamp when the broadcast is scheduled to send |
| `scheduled_timezone` | Timezone for the scheduled send time (e.g., "America/New_York") |
| `broadcast_recipients_count` | Number of recipient records created for this broadcast |
| `percentage_complete` | Sending progress percentage (0-100) |
| `tags` | Array of tags assigned to the broadcast |
| `segment_ids` | Array of segment IDs this broadcast targets |
| `excluded_segment_ids` | Array of segment IDs this broadcast never reaches |
| `email_server_ids` | Array of email server IDs used for sending |
| `created_at` | Timestamp when the broadcast was created |
| `updated_at` | Timestamp when the broadcast was last updated |

## List Broadcasts

```
GET /api/v1/broadcasts
```

Retrieve a list of all broadcasts for the authenticated broadcast channel.

### Query Parameters

- `limit` (optional): Maximum number of broadcasts to return, from 1 to 1000
- `offset` (optional): Number of broadcasts to skip, 0 or more

A `limit` or `offset` outside those ranges, or that is not a whole number, returns `422 Unprocessable Entity`.

### Request

```bash
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/broadcasts
```

### Response

```json
{
  "data": [
    {
      "id": 1,
      "name": "Weekly Newsletter",
      "subject": "Your Weekly Update",
      "status": "draft",
      "total_recipients": 0,
      "sent_at": null,
      "created_at": "2024-01-01T12:00:00Z"
    }
  ],
  "total": 1
}
```

## Get Broadcast

```
GET /api/v1/broadcasts/:id
```

Retrieve details of a specific broadcast.

### Request

```bash
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/broadcasts/123
```

### Response

```json
{
  "id": 123,
  "name": "Weekly Newsletter",
  "subject": "Your Weekly Update",
  "body": "<p>Newsletter content here</p>",
  "reply_to": "support@example.com",
  "status": "draft",
  "track_opens": true,
  "track_clicks": true,
  "total_recipients": 150,
  "sent_at": null,
  "created_at": "2024-01-01T12:00:00Z",
  "updated_at": "2024-01-01T12:00:00Z"
}
```

## Create Broadcast

```
POST /api/v1/broadcasts
```

### Parameters

- `subject` (required): Subject line of the email
- `body` (required): Content of the email, which can be either a simple string or HTML
- `name` (optional): Internal name of the broadcast. Omitted, it takes the subject
- `preheader` (optional): Preview text that appears in email clients
- `html_body` (optional): Whether the body contains HTML (default: false)
- `content_type` (optional): `rich_text`, `html` or `blocks`. With `blocks`, send the design as `blocks_json` and omit `body`; Broadcast renders the HTML itself
- `blocks_json` (optional): A block document (`{ "version": 1, "blocks": [...] }`) for the [Drag-and-Drop Email Builder](https://sendbroadcast.net/docs/block-email-builder)
- `reply_to` (optional): Reply-to email address
- `sender_identity_id` (optional): ID of the sender identity to send as (must belong to the same channel); omit to use the channel default
- `track_opens` (optional): Whether open tracking is enabled. Left out, it uses the channel default (`tracking.track_opens` in the [Channel Settings API](https://sendbroadcast.net/docs/api-channel-settings)), which is `false` unless you changed it.
- `track_clicks` (optional): Whether click tracking is enabled. Left out, it uses the channel default `tracking.track_clicks` (`false` unless you changed it).
- `scheduled_send_at` (optional): ISO 8601 datetime for scheduled sending
- `scheduled_timezone` (optional): Timezone for scheduled sending
- `segment_ids` (optional): Array of segment IDs to target
- `excluded_segment_ids` (optional): Array of segment IDs this broadcast must never reach. The audience becomes (segments) AND topic AND NOT (excluded segments), retries included, and every count uses the same rule. With no `segment_ids` the exclusion applies to all subscribers. Must be segments of this channel and not also in `segment_ids` (422 otherwise). `[]` removes them
- `topic_id` (optional): Send only to subscribers who receive this [Subscriber Topics](https://sendbroadcast.net/docs/subscriber-topics) topic. The audience becomes (segments) AND topic, and the email's unsubscribe link turns off only this topic unless the channel unsubscribes from everything. `null` removes it
- `email_server_ids` (optional): Array of specific email server IDs to use
- `html_body_add_footer` (optional): For `content_type: "html"`, whether the channel footer with the unsubscribe link is appended (default: true). An email with its own unsubscribe link (a footer block that shows the link, or a `{{ unsubscribe_url }}` link that every subscriber sees) never gets the channel footer on top, whatever this value (2.46.0 or later). Without an own link, rich text and block emails always get the channel footer, and HTML emails follow this value. Returned on every broadcast. A value that disagrees with the document is replaced, and the response carries a `parameter_overridden` warning (see [API Response Warnings](https://sendbroadcast.net/docs/api-response-warnings#the-footer-of-a-block-email)).
- `taggify_list` (optional): Comma-separated list of tags to assign to the broadcast
- `use_any_email_server` (optional): Allow using any available email server
- `email_headers` (optional): Array of custom email headers, each with `key` and `value` fields

### Request

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "broadcast": {
      "subject": "Test Subject",
      "preheader": "Test Preheader",
      "body": "<p>Test Body</p>",
      "name": "Test Name",
      "reply_to": "support@example.com",
      "track_opens": true,
      "track_clicks": true
    }
  }' \
  http://your-domain.com/api/v1/broadcasts
```

Take note that the `body` field should be a valid HTML string. If it is not a valid HTML string, the broadcast's rendered body will be malformed.

When you edit a draft broadcast, the application will attempt to fix any malformed HTML, but this is not always possible.

In general, line breaks should be represented as `
` tags, and paragraphs should be wrapped in `

` tags. If you reference images, you must ensure that the image URL is valid. You do not need to include the head or body tags in the body field. Broadcast will automatically wrap the content of your body during the outbound mail processing. ### Response
```json
{
  "id": 123
}
```

 ## Update Broadcast
```
PATCH /api/v1/broadcasts/:id
```

 Update an existing broadcast. Only broadcasts in `draft` or `scheduled` status can be updated. ### Request
```bash
curl -X PATCH \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "broadcast": {
      "subject": "Updated Subject",
      "body": "<p>Updated content</p>"
    }
  }' \
  http://your-domain.com/api/v1/broadcasts/123
```

 ### Response
```json
{
  "id": 123,
  "name": "Updated Newsletter",
  "subject": "Updated Subject",
  "body": "<p>Updated content</p>",
  "reply_to": "support@example.com",
  "status": "draft",
  "created_at": "2024-01-01T12:00:00Z",
  "updated_at": "2024-01-01T12:30:00Z"
}
```

 ## Delete Broadcast
```
DELETE /api/v1/broadcasts/:id
```

 Delete a broadcast. Only broadcasts in `draft` or `scheduled` status can be deleted. ### Request
```bash
curl -X DELETE \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/broadcasts/123
```

 ### Response
```json
{
  "message": "Broadcast deleted successfully"
}
```

 ## Send Broadcast
```
POST /api/v1/broadcasts/:id/send_broadcast
```

 Queue a broadcast for immediate sending. The broadcast must be in `draft` or `failed` status and must be sendable (have required content). ### Request
```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/broadcasts/123/send_broadcast
```

 ### Response
```json
{
  "id": 123,
  "message": "Broadcast queued for sending",
  "status": "queueing"
}
```

 ## Schedule Broadcast
```
POST /api/v1/broadcasts/:id/schedule_broadcast
```

 Schedule a broadcast for future sending at a specific date and time. The broadcast must be in `draft` or `failed` status and must be sendable (have required content). Once scheduled, the broadcast will be automatically sent when the scheduled time arrives. ### Parameters - `scheduled_send_at` (required unless already set on broadcast): ISO 8601 datetime or datetime string (e.g., "2024-01-15 14:30") - `scheduled_timezone` (required unless already set on broadcast): IANA timezone identifier (e.g., "America/New_York", "Europe/London", "UTC") You can either pass the schedule parameters in the request body, or set them beforehand when creating/updating the broadcast. ### Request
```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "broadcast": {
      "scheduled_send_at": "2024-01-15 14:30",
      "scheduled_timezone": "America/New_York"
    }
  }' \
  http://your-domain.com/api/v1/broadcasts/123/schedule_broadcast
```

 ### Response
```json
{
  "id": 123,
  "message": "Broadcast scheduled for future sending",
  "status": "future_scheduled",
  "scheduled_send_at": "2024-01-15T19:30:00.000Z",
  "scheduled_timezone": "America/New_York"
}
```

 ### Common Timezones

| Timezone | Description |
| --- | --- |
| `America/New_York` | Eastern Time (US) |
| `America/Chicago` | Central Time (US) |
| `America/Denver` | Mountain Time (US) |
| `America/Los_Angeles` | Pacific Time (US) |
| `Europe/London` | Greenwich Mean Time / British Summer Time |
| `Europe/Paris` | Central European Time |
| `Asia/Tokyo` | Japan Standard Time |
| `UTC` | Coordinated Universal Time |

## Cancel Schedule

```
POST /api/v1/broadcasts/:id/cancel_schedule
```

Cancel a scheduled broadcast, returning it to draft status. The broadcast must be in `future_scheduled` status.

### Request

```bash
curl -X POST \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/broadcasts/123/cancel_schedule
```

### Response

```json
{
  "id": 123,
  "message": "Scheduled broadcast cancelled",
  "status": "draft"
}
```

## Get Broadcast Statistics

```
GET /api/v1/broadcasts/:id/statistics
```

Retrieve comprehensive statistics for a broadcast including delivery metrics, engagement data, and issue tracking.

### Required Permission

- **Read Permission**: Required to access statistics

### Request

```bash
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  http://your-domain.com/api/v1/broadcasts/123/statistics
```

### Response

```json
{
  "broadcast_id": 123,
  "broadcast_name": "Weekly Newsletter",
  "broadcast_subject": "Your Weekly Update",
  "status": "sent",
  "total_recipients": 10000,
  "sent_at": "2024-01-10T12:00:00Z",
  "begin_send_at": "2024-01-10T12:00:00Z",
  "created_at": "2024-01-09T10:00:00Z",
  "tracking": {
    "track_opens": true,
    "track_clicks": true
  },
  "delivery": {
    "sent": 9850,
    "delivered": 9790,
    "failed": 150,
    "delivery_rate": 98.5
  },
  "engagement": {
    "opens": {
      "count": 4500,
      "rate": 45.0
    },
    "clicks": {
      "count": 1200,
      "rate": 12.0
    },
    "unique_links_clicked": 0
  },
  "issues": {
    "bounces": {
      "count": 150,
      "rate": 1.5
    },
    "complaints": {
      "count": 5,
      "rate": 0.05
    },
    "unsubscribes": {
      "count": 25,
      "rate": 0.25
    }
  }
}
```

### Statistics Fields

- **broadcast_id**: Unique identifier of the broadcast
- **delivery.sent**: Number of recipients the broadcast was sent to
- **delivery.delivered**: Number of emails your provider confirmed delivered. Needs the provider's delivery webhooks; a plain SMTP server never reports it, so this stays 0
- **delivery.failed**: Number of failed sends
- **delivery.delivery_rate**: Confirmed deliveries as a percentage of recipients
- **engagement.opens**: Open tracking statistics (count and rate)
- **engagement.clicks**: Click tracking statistics (count and rate)
- **issues.bounces**: Bounce statistics (count and rate)
- **issues.complaints**: Spam complaint statistics (count and rate)
- **issues.unsubscribes**: Unsubscribe statistics (count and rate)

Note: Open and click statistics are only available if `track_opens` and `track_clicks` were enabled for the broadcast.

(Requires Broadcast 2.49 or later)

`engagement.opens` and `engagement.clicks` leave out mail captured by a test server, and their `rate` is a percentage of the recipients whose mail was not captured. The `Opens` and `Clicks` series of the timeline and the link statistics (`total_clicks`, `unique_clicks`, `click_rate`) leave it out too. Bounce, complaint and unsubscribe rates are still a percentage of every recipient.

## Get Broadcast Statistics Timeline

```
GET /api/v1/broadcasts/:id/statistics/timeline
```

Retrieve time-series data showing how engagement metrics evolved over time. Useful for visualizing broadcast performance in charts.

### Query Parameters

- `timeframe` (optional): Time period for the data. Default: `24h`
  - Options: `60m`, `120m`, `3h`, `6h`, `12h`, `18h`, `24h`, `48h`, `72h`, `7d`, `14d`
- `metrics` (optional): Comma-separated list of metrics to include
  - Options: `processed`, `delivered`, `opens`, `clicks`, `bounces`
  - Default: All metrics

### Request

```bash
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  "http://your-domain.com/api/v1/broadcasts/123/statistics/timeline?timeframe=24h&metrics=opens,clicks"
```

### Response

```json
{
  "broadcast_id": 123,
  "timeframe": "24h",
  "interval": "1h",
  "start_time": "2024-01-10T12:00:00Z",
  "end_time": "2024-01-11T12:00:00Z",
  "series": [
    {
      "name": "Opens",
      "data": {
        "2024-01-10T12:00:00Z": 100,
        "2024-01-10T13:00:00Z": 250,
        "2024-01-10T14:00:00Z": 320
      }
    },
    {
      "name": "Clicks",
      "data": {
        "2024-01-10T12:00:00Z": 25,
        "2024-01-10T13:00:00Z": 80,
        "2024-01-10T14:00:00Z": 110
      }
    }
  ]
}
```

### Timeframe Options

Each timeframe uses an appropriate interval for data aggregation:

| Timeframe | Interval | Use Case |
| --- | --- | --- |
| `60m` | 1 minute | Real-time monitoring during send |
| `120m` | 2 minutes | Short-term tracking |
| `24h` | 1 hour | Daily performance view |
| `7d` | 6 hours | Weekly trend analysis |
| `14d` | 12 hours | Long-term performance tracking |

## Get Broadcast Link Statistics

```
GET /api/v1/broadcasts/:id/statistics/links
```

Retrieve click statistics for links in the broadcast.

**Note:** Current implementation returns aggregate click counts only. Per-link statistics require additional database schema changes and are planned for a future release.

### Query Parameters

- `sort` (optional): Sort field. Default: `clicks`
  - Options: `clicks`, `url`, `unique_clicks`
- `order` (optional): Sort order. Default: `desc`
  - Options: `asc`, `desc`
- `limit` (optional): Maximum number of links to return. Default: 100, Max: 500

### Request

```bash
curl -X GET \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  "http://your-domain.com/api/v1/broadcasts/123/statistics/links?sort=clicks&order=desc&limit=10"
```

### Response

```json
{
  "broadcast_id": 123,
  "total_links": 5,
  "total_clicks": 1200,
  "links": [
    {
      "id": 1,
      "identifier": "abc123",
      "url": "https://example.com/product/123",
      "clicks": 450,
      "total_clicks": 450,
      "unique_clicks": 420,
      "click_rate": 4.2,
      "first_clicked_at": "2024-01-10T12:05:00Z",
      "last_clicked_at": "2024-01-11T08:30:00Z"
    },
    {
      "id": 2,
      "identifier": "def456",
      "url": "https://example.com/about",
      "clicks": 320,
      "total_clicks": 320,
      "unique_clicks": 290,
      "click_rate": 2.9,
      "first_clicked_at": "2024-01-10T12:10:00Z",
      "last_clicked_at": "2024-01-11T06:15:00Z"
    }
  ]
}
```

### Link Object Fields

- **id**: Unique identifier of the link redirect
- **identifier**: Short identifier used in tracked URLs
- **url**: The original destination URL
- **clicks**: Total number of clicks (same as total_clicks)
- **total_clicks**: Total number of clicks on this link
- **unique_clicks**: Number of unique recipients who clicked
- **click_rate**: Percentage of recipients who clicked this link
- **first_clicked_at**: Timestamp of the first click
- **last_clicked_at**: Timestamp of the most recent click

## Broadcast Status Values

- `draft`: Being composed, can be edited
- `future_scheduled`: Scheduled for future sending at a specific time
- `scheduled`: Queued and about to be sent
- `queueing`: Being prepared for sending
- `sending`: Currently being sent
- `sent`: Successfully sent
- `failed`: Failed to send
- `partial_failure`: Some recipients failed
- `aborted`: Manually stopped
- `paused`: Temporarily paused

## Error Responses

If the request is invalid, the response will include an appropriate HTTP status code and error message:

### 400 Bad Request

```json
{
  "error": "Broadcast is not ready to send"
}
```

### 401 Unauthorized

```json
{
  "error": "Unauthorized"
}
```

### 404 Not Found

```json
{
  "error": "Broadcast not found"
}
```

### 422 Unprocessable Entity

```json
{
  "error": "Subject can't be blank, Body can't be blank"
}
```
