Theme

Broadcasts API

A one-off send: its own email, a segment, and a time. Membership is resolved when it sends, not when it's scheduled.

Method & pathWhat it does
GET /api/v1/broadcastsAll of them. Filter with ?status=.
POST /api/v1/broadcastsCompose one. Body: name, segment_id, email_source, email_id, emails_per_hour.
GET /api/v1/broadcasts/:idOne, with its email, segment and stats.
PATCH /api/v1/broadcasts/:idUpdate name, segment_id, emails_per_hour.
DELETE /api/v1/broadcasts/:idOnly a draft or a canceled one. 409 otherwise.
POST /api/v1/broadcasts/:id/replace_emailSwap the email, while it's still a draft or scheduled.
POST /api/v1/broadcasts/:id/scheduleBody: send_at. See scheduling for how the time is read.
POST /api/v1/broadcasts/:id/unscheduleBack to draft, keeping its email and audience. 409 unless scheduled.
POST /api/v1/broadcasts/:id/send_nowSend it. Answers with the recipient count.
POST /api/v1/broadcasts/:id/cancelCancel it and everything it has queued.

A broadcast never shares its email

email_source is required and accepts only scratch or copy. shared is refused with 422, on the server, rather than trusted to whichever client is asking.

POST /api/v1/broadcasts
{ "email_source": "copy", "email_id": 42, "segment_id": 3, "name": "July announcement" }

What one broadcast sent should stay what it sent. If a broadcast pointed at a shared library row, editing a sequence step months later could silently rewrite the record of a send that already went out — so every broadcast gets a row of its own, blank or copied. This is also why emails/ in a manager repo fills up with rows that exist for one send: expected, not drift, and the used-in filter is how you tell them apart.

The email can only be swapped or forked while the broadcast is still a draft or scheduled. Once it's sending or sent, its email is history.

Scheduling

send_at takes any parseable datetime. A time without a UTC offset"2026-08-10T08:30" — is read in the account's timezone: the profile timezone setting, Eastern Time if none is set. Include an offset ("2026-08-10T08:30:00-04:00") to say exactly what you mean regardless of that setting. Responses always carry the offset. A time already in the past is refused with 422.

unschedule undoes scheduling and nothing else: the broadcast goes back to draft with its email and audience intact. cancel is the harder stop — it moves the broadcast to canceled and kills anything already queued, which is the tool once sending has started.

Delivery

Sending fans out through the ordinary paced queue — one queued email per recipient. Guards, send windows and tracking all apply, and every broadcast gets a full report.

The fan-out is spread across time at the broadcast's sending pace: emails_per_hour when the broadcast sets one, otherwise the account default (sending.broadcast_emails_per_hour, 5,000 by default). The pace is written into each queued row's due time at send time, so the queue shows the real schedule — 20,000 recipients at 5,000/hour is four hours of visible, cancelable rows. Set emails_per_hour to null to go back to the account default.

Audience

Resolved at send time from the segment's live rules, not stored when you schedule. A segment that grows between now and Friday means more recipients on Friday. Unsubscribed people are excluded, and the standing suppression guard checks again for every individual send.

Broadcasts are runtime rather than authoring, so they don't appear in a manager repo — though the emails they mint do.

See also: Emails API · Segments API · Broadcasts, for humans