# Broadcasts

Send one email to an audience or a segment of it, from the API or the dashboard, on a reputation pool kept separate from your transactional mail.

A broadcast is one message sent to everyone in an audience, or to a segment of that audience. You compose it, you can send a test to yourself, you preview it rendered against a real contact, and then you send it now or schedule it. It runs on the same verified domains and lands in the same delivery log as the mail your app sends, on a sending reputation deliberately kept apart from it.

## It cannot share a reputation with your receipts

Marketing mail here goes through its own AWS configuration set and its own IP pool, and the send path checks that before every broadcast: if the marketing and transactional pools would collide, the broadcast refuses to send rather than sending and quietly poisoning the address your password resets go out from. This is the failure that costs the most and it is the one thing on this page enforced by an exception rather than by a setting.

## Consent is checked per copy, at send time

Recipients are expanded from the audience when the send starts, but each individual copy re-checks suppression, membership status, and topic preference at the moment it is written. Somebody who unsubscribes forty seconds into a fan-out is not mailed. Every recipient who is skipped keeps a row with a reason in prose, so a send to 40,000 people that reaches 38,600 tells you what happened to the other 1,400 instead of leaving you to subtract.

## Unsubscribe is not optional and not yours to configure

Every broadcast copy carries List-Unsubscribe and List-Unsubscribe-Post: List-Unsubscribe=One-Click, the RFC 8058 pair that Gmail and Yahoo require of bulk senders. The token is stored on the membership row rather than derived from a secret, so it survives a key rotation and can be revoked for one person. A GET on the unsubscribe URL never unsubscribes anybody, because mail clients prefetch links; only a POST does. The unsubscribe link is excluded from click tracking.

## Test and preview before it is 40,000 people

POST to the test endpoint with your own address and you get the real thing: the real configuration set, the real merge fields, the real headers. It writes no recipient rows and moves no counters, and it still honours suppression. The preview endpoint renders the broadcast against a real contact and hands back the subject, the HTML, the text, the List-Unsubscribe headers, and a list of every merge field with whether that contact actually supplies it.

## A report that is per recipient, not just a percentage

The broadcast carries counters for recipients, sent, delivered, bounced, complained, failed, skipped, unique and total opens, unique and total clicks, and unsubscribes. Underneath them the recipients endpoint pages through every individual address with its status, the reason if it was skipped, the message id, and the timestamps it was sent, opened, and clicked at. That message id is a transactional message id: the same GET /v1/emails/:id lookup works on it.

## The API

| Endpoint | What it does |
| --- | --- |
| `POST /v1/broadcasts` | Create a draft from an audience id, a from address, and either html and text or a template id. |
| `PATCH /v1/broadcasts/:id` | Edit a draft. Content freezes when it sends. |
| `POST /v1/broadcasts/:id/test` | Send the real thing to one or more addresses. No recipient rows, no counters. |
| `GET /v1/broadcasts/:id/preview?email=` | Render it against one contact, with every merge field and whether it is supplied. |
| `POST /v1/broadcasts/:id/send` | Send now, or pass scheduled_at as an ISO timestamp to schedule it. |
| `POST /v1/broadcasts/:id/cancel` | Cancel a draft or a scheduled send. |
| `GET /v1/broadcasts/:id/recipients` | Every address with its status, reason, message id, and open and click times. |

## A broadcast, created and scheduled

Two calls: one to compose it, one to put it on the clock. Both need a key with the mail:send scope.

```bash
# Compose it against an audience, optionally narrowed by a segment.
curl -sS https://emails.sh/v1/broadcasts \
  -H "Authorization: Bearer $EMAILSSH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "March product update",
    "from": "Acme <news@acme.com>",
    "audience_id": "3f0c9a2e-7b41-4a0d-9c6b-1e2f5a8d4c30",
    "segment_id": "b1d7e4c8-2a93-4f65-8e07-9c3b6a1d5f28",
    "subject": "What shipped in March",
    "html": "<p>Hello {{ first_name }}, here is what changed.</p>",
    "text": "Hello {{ first_name }}, here is what changed."
  }'

# Send a real copy to yourself first. This moves no counters.
curl -sS https://emails.sh/v1/broadcasts/$BROADCAST_ID/test \
  -H "Authorization: Bearer $EMAILSSH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"to": "you@acme.com"}'

# Then put it on the clock.
curl -sS https://emails.sh/v1/broadcasts/$BROADCAST_ID/send \
  -H "Authorization: Bearer $EMAILSSH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scheduled_at": "2026-08-04T09:00:00Z"}'
```

## What this does not do

### There is no A/B testing

No subject line split, no content variants, no winner selection, no send-time optimisation. One broadcast is one subject and one body. If split testing is how your team works, this is a real reason to keep the tool you have.

### There is no drag-and-drop builder

You write HTML, or you store a template and reference it. Merge fields are {{ name }} and nothing else: no conditionals, no loops, no Liquid, no MJML. A missing field renders as empty rather than as the literal braces. If a non-developer needs to change the copy without a deploy, say so before you buy this.

### Sending is a fan-out, not a drip

A broadcast has one scheduled_at and then goes out in batches as fast as the queue drains. There is no per-hour throttle and no timezone-aware delivery window, so a send scheduled for 09:00 UTC arrives at 09:00 UTC for everybody.

### No recurring or RSS-driven campaigns

Every broadcast is composed and sent once. A weekly digest built from a feed is something you would drive yourself from your own scheduler against the API.

### No resend to non-openers

There is no one-click "send again to the people who did not open it". You can read the recipients endpoint, build a segment from engagement rules, and compose a second broadcast against it, which is three calls rather than one button.

## Questions

### Does a broadcast draw on my transactional email allowance?

No. The two products are priced separately: transactional email is priced by emails sent, and the marketing side is priced by contacts stored. Sends to your own audiences carry no per-email charge and do not count against a transactional plan.

### Can I send a broadcast to a segment rather than a whole audience?

Yes. Pass segment_id alongside audience_id when you create it. A segment can only narrow the audience, never widen it, and members outside it still get a recipient row marked skipped with the segment named as the reason.

### What happens if the same broadcast is queued twice?

Nothing sends twice. There is a unique index on the broadcast and the recipient address, and claiming a recipient is an update that only succeeds from the pending state, so a redelivered queue message is a no-op rather than a second copy.

### Can I edit a broadcast after scheduling it?

Cancel it, edit the draft, and schedule it again. Edits are accepted while it is a draft. Once it starts sending, the content is frozen onto the broadcast so that the report describes what actually went out.

### Do bounces from a broadcast affect my transactional sending?

A hard bounce or a complaint adds the address to the workspace suppression list, which is shared, so you will not keep mailing somebody who complained. The sending reputation is not shared: marketing and transactional use separate configuration sets and separate IP pools.

## Related

- [Audiences and segments](https://emails.sh/features/audiences-and-segments.md)
- [Automations](https://emails.sh/features/automations.md)
- [Templates](https://emails.sh/features/templates.md)
- [Analytics](https://emails.sh/features/analytics.md)

Docs: https://emails.sh/docs.md