Audience & broadcasts
Broadcasts
Send one marketing email to a segment with per-contact variables, an automatic unsubscribe footer, scheduling and consent checks.
A broadcast is one email sent to every eligible contact in a segment. Sooner personalizes it for each recipient, adds an unsubscribe link and your postal address, and skips anyone who has opted out. Use broadcasts for newsletters and announcements. Use POST /emails for transactional email.
What a broadcast needs
| Part | Required to send | Notes |
|---|---|---|
| Segment | Yes | The audience. A broadcast has exactly one segment. See Segments. |
| Topic | No | Limits the audience to contacts subscribed to that topic. See Topics. |
from | Yes | The address must be on a verified domain in the project. |
subject | Yes | Up to 998 characters. Variables are allowed. |
html or text | Yes, at least one | Send both for the best compatibility. |
reply_to, preview_text, name | No | preview_text is the inbox preview line, up to 500 characters. name is for your own reference. |
You can save a broadcast as a draft with only some of these fields, then complete it later. The check for the required fields happens when you send.
Create and send
In the dashboard, open Broadcasts, create one, write it and send it. Sending needs the admin role. With the API, use a full_access key. This creates a draft:
curl -X POST https://api.sooner.dev/broadcasts \
-H "Authorization: Bearer $SOONER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "November newsletter",
"segment_id": "jd5f2h8k4m7p1r6t9v3x0z2c",
"topic_id": "jt3b6n9q2w5e8r1y4u7i0o2p",
"from": "Acme <news@mail.yourcompany.com>",
"subject": "What is new at Acme",
"preview_text": "Three things we shipped this month",
"html": "<p>Hi {{{FIRST_NAME|there}}},</p><p>Here is what is new.</p>",
"text": "Hi {{{FIRST_NAME|there}}}, here is what is new."
}'{ "id": "jb4m7q2x5c8v1n6z9w3e0r2t" }Send it when you are ready:
curl -X POST https://api.sooner.dev/broadcasts/jb4m7q2x5c8v1n6z9w3e0r2t/send \
-H "Authorization: Bearer $SOONER_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'{ "id": "jb4m7q2x5c8v1n6z9w3e0r2t" }To create and send in one request, add "send": true to the create call. If validation fails, nothing is created: the request returns the error and no draft is left behind.
Sending returns as soon as the broadcast is accepted. Delivery to the segment continues in the background.
Variables
Subject, HTML and text can contain variables in the same triple-brace syntax as templates, filled in separately for each recipient:
| Variable | Value |
|---|---|
{{{FIRST_NAME}}} | The contact's first name. |
{{{LAST_NAME}}} | The contact's last name. |
{{{EMAIL}}} | The contact's email address. |
{{{RESEND_UNSUBSCRIBE_URL}}} | A signed link to the hosted preferences page for that contact. {{{SOONER_UNSUBSCRIBE_URL}}} is an alias. |
{{{company}}} | Any contact property you declared, by its key. |
Add an inline fallback with {{{FIRST_NAME|there}}}. It is used when the contact has no value. For a property, the property's own fallback_value is used before an inline fallback. Keys are matched without regard to case. In HTML, values are escaped, and the unsubscribe URL is inserted as is.
The unsubscribe footer and postal address
Every broadcast must let the recipient opt out, and marketing email must carry the sender's physical address. Sooner handles both:
- Unless you place the unsubscribe variable yourself, Sooner appends a footer to the HTML body with your postal address and an Unsubscribe link. It appends the same information to the text body. The HTML and text bodies are checked separately.
- If your HTML or text already contains
{{{RESEND_UNSUBSCRIBE_URL}}}, Sooner does not add a footer to that body. In that case you are responsible for including your postal address. - Every broadcast email also carries
List-UnsubscribeandList-Unsubscribe-Postheaders, so Gmail, Yahoo and other clients show a native unsubscribe button. See Unsubscribes.
The postal address comes from your team settings. Set it in the dashboard under Settings, in the Postal address card. Without one, the footer contains only the unsubscribe link, so set it before your first broadcast. Changing it applies to broadcasts sent afterward.
preview_text is added to the HTML body as hidden text at the top, so inboxes show it next to the subject.
Who receives a broadcast
Sooner reads the segment's members when sending starts, in pages of 200 contacts. A contact is skipped, with no email created for them, when any of these is true:
| Reason | Details |
|---|---|
| Unsubscribed | The contact has unsubscribed: true. |
| Opted out of the topic | The broadcast has a topic_id and the contact chose opt_out, or has no choice and the topic defaults to opt_out. |
| Suppressed | The address is on your team's suppression list. |
| Deleted | The contact was deleted after it joined the segment. |
Preferences and suppressions are checked again right before each email goes out. A contact who unsubscribes while a large broadcast is sending is not emailed afterward.
Each recipient who is sent a message appears as its own email in your logs, with its own status and events, and with last_event such as delivered or opened.
Scheduling
Add scheduled_at to the create call with "send": true, or to the send call. It accepts ISO 8601, or text such as in 2 hours, the same as email scheduling.
curl -X POST https://api.sooner.dev/broadcasts/jb4m7q2x5c8v1n6z9w3e0r2t/send \
-H "Authorization: Bearer $SOONER_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "scheduled_at": "2026-11-20T09:00:00Z" }'- The time must be at most 30 days ahead. A time in the past, or within about a second, sends immediately.
- A scheduled broadcast has status
scheduled. Cancel it withPOST /broadcasts/{id}/cancelbefore it starts. Once it starts, it cannot be canceled. - A canceled broadcast cannot be sent again. Duplicate it with
POST /broadcasts/{id}/duplicate, which creates a new draft named "(copy)".
Statuses
| Status | Meaning |
|---|---|
draft | Editable and not sent. Only drafts can be edited or sent. |
scheduled | Waiting for scheduled_at. |
queued | Accepted and about to start. |
sending | Being delivered to the segment. |
sent | Every page of the segment has been handed off for sending. This does not mean every email was delivered. |
canceled | A scheduled broadcast was canceled. |
failed | Sending stopped. Common causes are in the next section. |
Limits and failures
- Each recipient that is sent counts toward your monthly quota. On Free, the daily cap of 500 also applies. A broadcast that reaches a cap stops with status
failed, and the recipients already handed off are still sent. Paid plans keep sending and are billed for overage. - If the sending domain is no longer verified, or the team is suspended, the broadcast fails.
- A draft can be deleted. A scheduled broadcast is canceled and then deleted. A broadcast that has started or finished cannot be deleted.
Broadcasts are sent on a separate queue from transactional email, so a large broadcast does not delay your receipts and password resets.
See the broadcasts API reference for every endpoint.