Message .broadcast
Email a whole Audience, such as your waitlist, in one call.
- Action class
send- Approval
- Sends on its own
- Emits
Message.broadcasted- Sites
- Gmail, Outlook
- Price
- Free
POST https: //api.sb/ {your-domain}/ messages/ broadcast
Sends one email to a whole Audience, such as your waitlist, now or at a set time. The hub’s definition of Message.broadcast.
Inputs
| Name | Type | Default | Meaning |
|---|---|---|---|
message | Message | Required | The Message to send. |
audience | Audience | Required | Who receives it. |
at | datetime | now | When it goes out; omitted, now. Unset: as soon as its delay window closes. |
Outputs
| Field | Type | Unit | What null means |
|---|---|---|---|
messages | Message[] | None. | Never null: one per Contact it reaches. In try mode, the first rendered Message only. |
message | Message | None. | Never null. |
counts.audience | integer | people | Never null: everyone the Audience selects when the call runs. |
counts.sending | integer | people | Never null: the Audience less both skips. |
counts.skippedNoConsent | integer | people | Never null: Contacts with no Consent for the Message’s purpose. |
counts.skippedSuppressed | integer | people | Never null: Contacts on a Suppression, from any startup of the Company. |
Effects
- Action class
send: an approval rule onsendcan make it wait for your OK- Modes
try: Counts who it would reach after the Consent and Suppression skips and renders the first Message; nothing sends.send: Returns a pending Action while its delay window runs; then each Message goes out from the broadcast stream.do: Holds the call for the Mode’s hold time, then returns202with the pending Action.
- Idempotency
Only with a key: the same
Idempotency-Keyand input return the first Action, and the same key with other input returns409. Each Contact gets the Message once per broadcast: a retry never sends a second copy to anyone it reached.- Saved or shared
Each sent Message is saved on its Contact, and every reply threads to it.
- Emits
Message.broadcastedwith{ message,audience, counts } - Performed by
- Price
Free on your own account: the platform charges nothing for the call.
- Standing approval
It goes out on its own, inside the Limits of your standing approval: a
Mandategranted withMandate.authorize. One approval rule makes it wait for your OK.
On each Venue
Gmail
Through Gmail.
| Our field | Gmail’s field |
|---|---|
Message.from | raw: (Your address, or a send-as alias you set up in Gmail) |
Message.to | raw: |
Message.subject | raw: (A reply keeps the thread’s subject, so Gmail threads it) |
Message.body | raw: |
Message.purpose | raw: (A commercial purpose adds the one-click unsubscribe headers Gmail shows as a link) |
- Sends from one inbox. 2,000 messages in a rolling 24 hours on a Google Workspace account, to at most 10,000 recipients; the rest wait for the window with
quota-reached. - Outside recipients. 3,000 addresses outside your own domain a day, and 500 on one message.
- Replies. Gmail pushes each new message within seconds; the platform renews the mailbox watch daily, inside the 7 days Gmail allows.
Outlook
Through Outlook.
| Our field | Outlook’s field |
|---|---|
Message.to | message.toRecipients |
Message.subject | message.subject |
Message.body | message.body (contentType html or text) |
Message.purpose | message.internetMessageHeaders (A commercial purpose adds the one-click unsubscribe headers) |
- Recipients from one mailbox. 10,000 in a rolling 24 hours on Exchange Online; past it the mailbox sends nothing until the oldest sends age out, so the call returns
quota-reachedwith the time. - Send rate. 30 messages a minute; the platform paces a broadcast under it, so Microsoft never throttles a send.
What try does
| Account | What try does |
|---|---|
| Gmail | Renders the exact email, headers included, and returns it with the time it would send; nothing reaches Gmail and no draft is saved. |
| Outlook | Renders the exact email or invite and returns it with the time it would go; nothing reaches Outlook and no draft or event is saved. |
Refuses when
| Check | Error | Fix | Then call |
|---|---|---|---|
| A broadcast past three times the trailing 7-day Message rate in an hour | spam-brake | Send to a smaller List, or wait an hour for the rate to fall. | Message.broadcast |
| A send from a Domain whose DNS records do not resolve | domain-authenticated | Add the 3 DNS records shown; it passes once they resolve, often within an hour. | Domain.authenticate |
Every call through an account can also meet the shared account refusals: grant-missing, needs-reauth, account-not-connected, over-mandate, quota-reached, venue-rejected.
The spam-brake payload
{
"type": "https://startupbuilder.dev/errors/spam-brake" ,
"title": "Sending too fast",
"status": 422,
"detail": "A broadcast past three times the trailing 7-day Message rate in an hour.",
"fix": "Send to a smaller List, or wait an hour for the rate to fall." ,
"next": {
"verb": "Message.broadcast",
"input": {}
},
"instance": "https://api.sb/leaseline.app/actions/action_1"
}When it waits
| Fact | Value |
|---|---|
send returns | A pending Action, reason:, while its delay window runs; Message.broadcasted fires when the last Message is handed off. |
| Delivered by | Message.broadcasted |
Example
Email the launch-day note to my waitlist at 8 a.m. Pacific on November
4.To retry, send the same Idempotency-Key: the retry returns the first answer for $0. A new key reads again.
Test values
A sk_test_ key answers these the same way every time, at $0 (every test value).
| Test value | Always returns | What it is for |
|---|---|---|
audience_test_waitlist | {"counts": | Four test Contacts, one of them on a Suppression, so your agent sees the skip without sending to anyone. |
audience_test_spam_rate | Refuses with spam-brake | Sends as if the Domain’s complaint rate were over the brake, so the broadcast refuses and nothing goes out. |
maxCost: | Refuses with max-cost | Any priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap. |
rate-limited.test | Refuses with rate-limited | Any call naming this hostname answers 429 with Retry-After, so your retry logic runs. |
sources-busy.test | Refuses with sources-busy | Any call naming this hostname answers 503 as if every route were busy, and nothing is spent. |