Audiences & broadcasts

Create an audience, add members by address or contact, and broadcast one email to every active member from a single inbox.

Group the people you email into an audience, then send to all of them with one call. Dairo fans the broadcast out in the background and skips anyone who previously reported your mail as spam or whose address hard-bounced. Audiences are built for opted-in sending — onboarding cohorts, customer announcements, internal notices — not cold outreach.

Reading audiences uses the audiences:read scope; creating, adding members, and deleting use audiences:write. Sending a broadcast is a send like any other — it needs messages:send. In the Python SDK, audiences live under dairo.lists.

Create an audience

POST /v1/audiences takes a name (up to 120 characters) and an optional description.

curl -X POST https://api.dairo.app/v1/audiences \
  -H "Authorization: Bearer $DAIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Beta announcements" }'
{
  "object": "audience",
  "id": "aud_123",
  "name": "Beta announcements",
  "description": null,
  "status": "active",
  "memberCount": 0,
  "createdAt": "2026-06-12T10:00:00Z",
  "updatedAt": "2026-06-12T10:00:00Z"
}

Add members

POST /v1/audiences/{audienceId}/members takes up to 2,000 members per request. Each member is exactly one of:

  • a raw handle — an email address, with an optional name, or
  • a contactId — a contact whose primary email address becomes the member's address. An unknown contact is a 404; a contact without an email handle is a 422.

Members are upserted by address: re-adding someone updates their name and metadata instead of creating a duplicate. The response reports how many members the request imported.

curl -X POST https://api.dairo.app/v1/audiences/aud_123/members \
  -H "Authorization: Bearer $DAIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "members": [ { "handle": "ada@example.com", "name": "Ada Lovelace" } ] }'
{ "audienceId": "aud_123", "imported": 1 }

Inspect an audience

List your audiences with GET /v1/audiences, or fetch one to see its active members.

curl https://api.dairo.app/v1/audiences \
  -H "Authorization: Bearer $DAIRO_API_KEY"

curl https://api.dairo.app/v1/audiences/aud_123 \
  -H "Authorization: Bearer $DAIRO_API_KEY"

Send a broadcast

POST /v1/audiences/{audienceId}/send emails every active member from the inbox you name. The body takes the same inboxId, subject, body, and attachments fields as a single send — exactly one of text, html, react, or a stored template — but no to, because the recipients come from the audience. replyTo, custom headers, and tags apply to every member's message. Fields that only make sense for one message — to, cc, bcc, contactId, channel, sendAt, the reply-threading fields (replyToMessageId, inReplyTo, references), and dryRun — are refused with a 400 naming the field, instead of being copied to every member or silently dropped, and so are the Telegram-only buttons and telegram payloads. A broadcast always sends now. Members are reached by email, so inboxId must be an email inbox: a Telegram or Slack inbox is a 400, and an inbox that is not yours (or not one a restricted key is pinned to) is refused before anything is queued.

A broadcast is asynchronous. Dairo validates the request and pins the message once — a stored template resolves at submit, so a bad template or variable is a 400 now, not a failure mid-fan-out — then returns 202 and delivers to each member in the background:

{
  "object": "broadcast",
  "id": "bcast_9f8c2a1b4e7d",
  "audienceId": "aud_123",
  "status": "queued"
}

Each member's delivery becomes its own outbound message, so bounces and complaints surface per recipient in delivery tracking.

curl -X POST https://api.dairo.app/v1/audiences/aud_123/send \
  -H "Authorization: Bearer $DAIRO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: beta-open-2026-06" \
  -d '{
        "inboxId": "inbox_7c9e6679",
        "subject": "We are opening the beta",
        "html": "<p>You are in. Here is how to get started…</p>"
      }'

Suppressed members are skipped. Every broadcast leaves out members who reported your mail as spam or whose address hard-bounced, which protects your sender reputation; they are only contacted if you deliberately pass ignoreComplaints: true. See Land in the inbox.

Retrying a broadcast is safe. A re-sent request with the same Idempotency-Key — or an identical request with no key at all — de-duplicates per recipient instead of emailing the whole audience twice. To deliberately send the same content to the same audience again, pass a new key. See Retries & idempotency.

Delete an audience

Deleting an audience archives it so it can no longer be sent to.

curl -X DELETE https://api.dairo.app/v1/audiences/aud_123 \
  -H "Authorization: Bearer $DAIRO_API_KEY"

Send responsibly

  • Only add people who opted in. Importing scraped or purchased addresses generates spam complaints and damages your domain's reputation for every inbox you send from.
  • Review the copy before a large send. Especially for agent-written copy, have a person read the message before it goes to everyone.
  • Prune addresses that bounce. Hard-bounced addresses are skipped automatically; remove them, and addresses that keep soft-bouncing, to keep the audience accurate.

Next steps

  • Templates — write one email and reuse it across every broadcast.
  • Webhooks — get per-recipient delivery events pushed to your app.
  • API reference — every audiences endpoint, with copy-paste requests.

On this page