Send a broadcast to a segment
Marketing sends to a segment or topic: drafts with blocks or HTML, consent at send time, scheduling, per-domain throttling, subject A/B tests, test sends, progress, analytics and cancel.
A broadcast is one marketing email to an audience: a segment, a topic, or a segment narrowed to a topic's subscribers. You write a draft, send a test, then send it now or at a time. The platform takes a snapshot of who may receive it, sends one email per contact through the normal sending pipeline, and keeps count of what happened.
A draft
curl https://api.minket.com/v1/broadcasts \
-H "Authorization: Bearer $MINKET_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "October update",
"from": "Acme <news@news.acme.example>",
"reply_to": "hello@acme.example",
"subject": "What changed in October, {{first_name}}",
"preview_text": "Three new features and one fix.",
"segment_id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5d",
"topic_id": "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5b",
"document": {
"version": 1,
"settings": { "footer": "Acme Ltd, 1 Example Street, Example City" },
"blocks": [
{ "type": "heading", "level": "1", "text": "Hello {{first_name}}" },
{ "type": "paragraph", "text": "Here is what changed on your **{{plan}}** plan." }
]
}
}'const draft = await minket.broadcasts.create({
name: "October update",
from: "Acme <news@news.acme.example>",
subject: "What changed in October, {{first_name}}",
segment_id: "019a2b3c-4d5e-7f60-8a9b-0c1d2e3f4a5d",
html,
});
await minket.broadcasts.test(draft.id, { to: ["me@acme.example"] });
await minket.broadcasts.send(draft.id, { scheduled_at: "tomorrow at 9am" });- Content is a block document or
html(plus an optionaltextpart); new content of one kind replaces the other. Variables are the contact's fields and properties, escaped as in templates. - Every email ends with an unsubscribe link and a preferences link, signed for that contact (with the broadcast's topic, the unsubscribe link leaves only that topic). If the content places
{{unsubscribe_url}}itself, only the preferences link is added. frommust be on a verified marketing domain of the project; a transactional domain is refused withstream_rule_violation. Marketing content checks apply.PATCHchanges a draft;DELETEremoves a draft, a canceled or failed broadcast, or a scheduled one (it is canceled first).POST /v1/broadcasts/{id}/duplicatecopies any broadcast, a sent one included, into a new draft named<name> (copy): sender, reply-to, subject, preview text, audience, content, throttle and the A/B test's variants and settings (not its winner). It answers201with the draft; nothing about the original's sending carries over. To change a broadcast that is no longer a draft, duplicate it.
Who gets it
When sending starts, the audience is resolved to a snapshot (GET /v1/broadcasts/{id}/recipients) of the contacts who may receive marketing mail right then: subscribed (not unsubscribed), active (not bounced, complained or sunset), opted in to the topic (their own choice, else the topic's default) and with no marketing suppression in force. The console shows the same count live while you pick the audience.
Consent and suppression are checked again for each recipient when its email is created, and the send service checks suppressions, the topic and the kill switch once more. A contact who unsubscribed or was suppressed after the snapshot is skipped with a reason (unsubscribed, suppressed, topic_opt_out, the contact's status) and gets nothing.
Each email is a normal email (source: "broadcast", with broadcast_id, contact_id and topic_id), with its own timeline, events and webhooks. A recipient is marked sent in the same transaction that creates its email, so a step that runs twice never sends anyone twice.
Sending, scheduling and throttling
POST /v1/broadcasts/{id}/sendanswers202 { "object": "broadcast", "id", "status" }. Withoutscheduled_at(as for emails, up to 30 days ahead) it starts now. Sending a scheduled broadcast again moves it to the new time.POST /v1/broadcasts/{id}/cancelstops a scheduled, sending or paused broadcast at once: nothing more is sent, what was sent stays sent, and it cannot be resumed. Its emails that SES has not taken yet (held by the kill switch, or still queued) are canceled when the send worker reaches them, with anemail.canceledevent each, and their recipients areskippedwith the reasoncanceled.statusmoves throughdraft,scheduled,queued(the snapshot is being taken),testing(an A/B sample is out),sending, thensent.paused,canceledandfailedare the other ends.broadcast.startedandbroadcast.completedevents mark the edges.
Broadcasts are throttled per sending domain. Each one sends at most its throttle_per_minute (600 by default, 1 to 10,000), and every broadcast from the same domain draws on one per-minute budget. That budget never goes above half of the platform's sending rate, so transactional mail always has room.
Pauses
A broadcast pauses where it stopped, with the reason in error, when:
- the project's kill switch is on: clear it, then send the broadcast again to resume;
- the project reached its daily sending cap: it resumes by itself at midnight UTC, or raise the cap and send it again;
- its domain stopped passing (not verified, authentication failing, sending turned off): fix the domain, then send it again.
Resuming carries on with the same snapshot; recipients already sent are never sent again.
A/B tests
{
"ab_test": {
"variants": [
{ "key": "a", "subject": "What changed in October" },
{ "key": "b", "subject": "Three new features" }
],
"sample_percent": 20,
"window_minutes": 120,
"metric": "opens"
}
}sample_percent (5 to 50) of the snapshot is picked at random and split evenly between the two subjects. The sample's emails are tracked for the metric whatever the domain's tracking setting. Once the sample is out, the test window (window_minutes, 15 minutes to 24 hours) starts; when it ends, the subject with more unique opens (or clicks) per email sent wins (a tie goes to a), ab_test.winner and ab_test.results are set, and everyone else gets the winning subject. Opens flatter whichever variant reached more Apple Mail users (why), so choose clicks when the email has a link worth clicking.
Test sends
POST /v1/broadcasts/{id}/test with { "to": ["me@acme.example"] } sends the current content to up to five addresses, each as its own email with [Test] before the subject. Test copies are not part of the broadcast: they are not counted and do not change its status. An address that is a contact gets its own variables and working links; any other address gets links that open the recipient pages without changing anything.
Progress and analytics
GET /v1/broadcasts/{id} carries stats:
| Field | What it counts |
|---|---|
recipients |
Contacts in the snapshot. |
sent, pending, skipped, failed |
Recipients whose email was created, who are still waiting, who were left out at send time, or whose email could not be created. |
delivered, opened, clicked, bounced, complained |
The broadcast's emails with at least one such event (each email once). |
unsubscribed |
Recipients who unsubscribed from marketing mail, or from the broadcast's topic, after their email went out. |
The counts follow the broadcast's emails as their events arrive. Once a broadcast has finished, a copy of its counts is kept with it, so they survive the recipient list's 13-month retention.
GET /v1/broadcasts/{id}/recipients lists the snapshot, newest contact first, with each recipient's status, email_id and sent_at. Filter it by status, by part of the address (email, any case), or by what happened to the email with event:
event |
Recipients | Adds |
|---|---|---|
sent, delivered, complained |
whose email had that event | |
opened |
whose email was opened | count, opens (once per day) |
clicked |
who clicked a link | count and clicked_links (url, clicks), clicks once per link and day |
bounced |
whose email bounced; bounce_type narrows it to permanent, transient or undetermined |
bounce_type |
unsubscribed |
who unsubscribed after it went out, as stats.unsubscribed counts |
|
suppressed |
skipped at send time, or refused at delivery, because the address was suppressed |
GET /v1/broadcasts/{id}/links lists the links recipients clicked, most clicked first: url, clicks (each recipient once per link and day) and unique_clicks (recipients). Each row's id is a cursor for after and before; it names no object.
Test keys
A test key's broadcasts send test-mode emails, which appear in the sandbox. Test keys see only test-mode broadcasts and live keys only live ones; the console shows both, marked.