Broadcast an email to an email list
Broadcasts one email to every active member of the list and returns 202 with a broadcast object: { "object": "broadcast", "id": "bcast_...", "audienceId", "status": "queued" }.
Broadcasts one email to every active member of the list and returns 202 with a broadcast object: { "object": "broadcast", "id": "bcast_...", "audienceId", "status": "queued" }. Recipients come from list membership; inboxId (the verified sending address) is required, along with a subject and exactly one body via text, html, react, or a stored template — a bad template reference fails synchronously with a 400. replyTo, headers, and tags apply to every member's message. Per-recipient fields — to, cc, bcc, contactId, channel, sendAt, replyToMessageId, inReplyTo, references, and dryRun — are refused with a 400 naming the field, since a broadcast always sends immediately to every active member; the Telegram-only buttons and telegram payloads are a 400 too. Members are reached by email, so inboxId must be an email inbox (a Telegram or Slack inbox is a 400). Delivery is asynchronous: each accepted recipient becomes an individual message emitting message.sent, then message.delivered, message.bounced, or message.complained, and a contact member with no current email handle is skipped. Each recipient is de-duplicated on a key derived from your idempotencyKey and their address (derived deterministically from the list and body when you omit one), so a retried broadcast never double-sends; recipients that previously complained are suppressed unless ignoreComplaints is true. Requires the messages:send scope.
Scope: messages:send. Send the API key as a bearer token; Scopes lists what each one reaches.
Request
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
audienceId | string (uuid) | yes | The audience's ID. |
Body
Required, application/json: BroadcastRequest.
The body of an audience broadcast. Every active member of the audience receives its own email from inboxId, which must be an email inbox. Per-recipient fields (to, cc, bcc, contactId, channel, sendAt, inReplyTo, references, replyToMessageId, dryRun) and the Telegram-only buttons and telegram are rejected with 400: members are the recipients, one message each, so none of them can apply.
| Field | Type | Required | Description |
|---|---|---|---|
inboxId | string | yes | |
subject | string | no | (default "") |
text | string | no | |
html | string | no | |
react | ReactRenderInput | no | |
template | MessageTemplateRef | no | |
attachments | array of SendMessageAttachment | no | Outbound send attachments. 'attachment' and safe 'auto' sends are inline; current inline API limit is 10 files, 8 MiB per file, 8 MiB total decoded bytes. 'link' is explicit and must be backed by a pre-created Dairo share link placed deliberately in text/html by the caller. (max items 10) |
idempotencyKey | string | no | Per-broadcast idempotency base: each member's message is keyed {idempotencyKey}:{address}, so re-posting the same broadcast never re-sends to a member already accepted. Omitted, the base is derived from the org, the audience and the body. (max length 128) |
replyTo | string (email) | no | Optional single Reply-To address, set verbatim as the MIME Reply-To header. E-mail only: a send with replyTo stays on e-mail even when every recipient is a Dairo inbox; channel a2a and Telegram or Slack inboxes answer 400. |
headers | map of string | no | Optional custom MIME headers ({name: value}). Spoofable/provenance headers are dropped server-side; malformed names or CR/LF are rejected. E-mail only: a send with headers stays on e-mail even when every recipient is a Dairo inbox; channel a2a and Telegram or Slack inboxes answer 400. |
tags | map of string | no | Optional SES message tags ({name: value}), validated against SES tag constraints. E-mail only: a send with tags stays on e-mail even when every recipient is a Dairo inbox; channel a2a and Telegram or Slack inboxes answer 400. |
ignoreComplaints | boolean | no | Override complaint and hard-bounce suppression for every member that previously complained or bounced. (default false) |
Response
202 Accepted. Body: application/json, Broadcast.
| Field | Type | Required | Description |
|---|---|---|---|
object | "broadcast" | yes | |
id | string | yes | The server-minted broadcast id (bcast_...). |
audienceId | string (uuid) | yes | |
status | "queued" | yes |
Errors
Every error has the same body, {"error": {"type", "code", "message", "param"}}; branch on code. Errors lists every code.
| Status | Meaning |
|---|---|
400 | The request is malformed or a value is invalid (invalid_request, invalid_domain, idempotency_key_mismatch). |
401 | The API key is missing or invalid. |
403 | The key lacks the scope this endpoint needs (scope_missing) or may not do this (not_permitted). |
404 | No such resource in this account (resource_not_found). |
422 | The request is well-formed but a field failed validation (validation_failed). |
429 | Rate limit, plan limit, budget or spend cap reached (rate_limited, plan_limit_reached, budget_exceeded, spend_cap_reached). |
500 | An unexpected error; retry with backoff, then contact support (internal_error). |