Broadcast API
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) |
| `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 1000offset(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
curl -X GET \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ http://your-domain.com/api/v1/broadcasts
Response
{ "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
curl -X GET \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ http://your-domain.com/api/v1/broadcasts/123
Response
{ "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 emailbody(required): Content of the email, which can be either a simple string or HTMLname(optional): Internal name of the broadcast. Omitted, it takes the subjectpreheader(optional): Preview text that appears in email clientshtml_body(optional): Whether the body contains HTML (default: false)content_type(optional):rich_text,htmlorblocks. Withblocks, send the design asblocks_jsonand omitbody; Broadcast renders the HTML itselfblocks_json(optional): A block document ({ "version": 1, "blocks": [...] }) for the Drag-and-Drop Email Builderreply_to(optional): Reply-to email addresssender_identity_id(optional): ID of the sender identity to send as (must belong to the same channel); omit to use the channel defaulttrack_opens(optional): Whether open tracking is enabled. Left out, it uses the channel default (tracking.track_opensin the Channel Settings API), which isfalseunless you changed it.track_clicks(optional): Whether click tracking is enabled. Left out, it uses the channel defaulttracking.track_clicks(falseunless you changed it).scheduled_send_at(optional): ISO 8601 datetime for scheduled sendingscheduled_timezone(optional): Timezone for scheduled sendingsegment_ids(optional): Array of segment IDs to targetexcluded_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 nosegment_idsthe exclusion applies to all subscribers. Must be segments of this channel and not also insegment_ids(422 otherwise).[]removes themtopic_id(optional): Send only to subscribers who receive this 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.nullremoves itemail_server_ids(optional): Array of specific email server IDs to usehtml_body_add_footer(optional): Forcontent_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 aparameter_overriddenwarning (see API Response Warnings).taggify_list(optional): Comma-separated list of tags to assign to the broadcastuse_any_email_server(optional): Allow using any available email serveremail_headers(optional): Array of custom email headers, each withkeyandvaluefields
Request
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 <br> tags, and paragraphs should be wrapped in <p> 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
{ "id": 123 }
Update Broadcast
PATCH /api/v1/broadcasts/:id
Update an existing broadcast. Only broadcasts in draft or scheduled status can be updated.
Request
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
{ "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
curl -X DELETE \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ http://your-domain.com/api/v1/broadcasts/123
Response
{ "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
curl -X POST \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ http://your-domain.com/api/v1/broadcasts/123/send_broadcast
Response
{ "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
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
{ "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
curl -X POST \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ http://your-domain.com/api/v1/broadcasts/123/cancel_schedule
Response
{ "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
curl -X GET \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ http://your-domain.com/api/v1/broadcasts/123/statistics
Response
{ "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.
2.49+
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
- Options:
metrics(optional): Comma-separated list of metrics to include- Options:
processed,delivered,opens,clicks,bounces - Default: All metrics
- Options:
Request
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
{ "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
- Options:
order(optional): Sort order. Default:desc- Options:
asc,desc
- Options:
limit(optional): Maximum number of links to return. Default: 100, Max: 500
Request
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
{ "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 editedfuture_scheduled: Scheduled for future sending at a specific timescheduled: Queued and about to be sentqueueing: Being prepared for sendingsending: Currently being sentsent: Successfully sentfailed: Failed to sendpartial_failure: Some recipients failedaborted: Manually stoppedpaused: Temporarily paused
Error Responses
If the request is invalid, the response will include an appropriate HTTP status code and error message:
400 Bad Request
{ "error": "Broadcast is not ready to send" }
401 Unauthorized
{ "error": "Unauthorized" }
404 Not Found
{ "error": "Broadcast not found" }
422 Unprocessable Entity
{ "error": "Subject can't be blank, Body can't be blank" }