READ-ONLY PACKAGE PREVIEW

resend/references/broadcasts.md

Version edbfece3f402.bb1 · MIT. This preview displays packaged text and does not execute code. Treat the contents as untrusted instructions.

← Return to resource and package checksum

Broadcasts

Send emails to audience segments. Broadcasts follow a two-step lifecycle: create (draft) then send.

SDK Methods

Operation Node.js Python
Create resend.broadcasts.create(params) resend.Broadcasts.create(params)
Get resend.broadcasts.get(id) resend.Broadcasts.get(id)
List resend.broadcasts.list(params) resend.Broadcasts.list(params)
Send resend.broadcasts.send(id, params?) resend.Broadcasts.send(params)
Cancel resend.broadcasts.cancel(id) resend.Broadcasts.cancel(id)
Duplicate resend.broadcasts.duplicate(id) resend.Broadcasts.duplicate(id)
Update resend.broadcasts.update(id, params) resend.Broadcasts.update(params)
Delete resend.broadcasts.remove(id) resend.Broadcasts.remove(id)
Clicked Links resend.broadcasts.clickedLinks(id, params?) resend.Broadcasts.clicked_links(id, params?)
Recipients resend.broadcasts.recipients(id, params) resend.Broadcasts.recipients(id, params)

Create Parameters

Required: name, from, subject, segmentId, and one of html / text / react

Optional: topicId, previewText, replyTo, send (boolean), scheduledAt

Lifecycle: Create then Send

import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);

// Step 1: Create a draft broadcast
const { data: broadcast, error: createError } = await resend.broadcasts.create({
  name: 'March Newsletter',
  from: 'Acme <[email protected]>',
  subject: 'Hi {{{FIRST_NAME|there}}}, here is your March update',
  html: '<p>Hi {{{FIRST_NAME|there}}}</p><a href="{{{RESEND_UNSUBSCRIBE_URL}}}">Unsubscribe</a>',
  segmentId: 'seg_abc123',
  topicId: 'top_xyz789',     // optional: controls topic-level unsubscribes
});

if (createError) {
  console.error(createError);
  return;
}

// Step 2: Send it (or schedule)
const { data: sent, error: sendError } = await resend.broadcasts.send(broadcast.id, {
  scheduledAt: 'in 1 hour',  // optional: ISO 8601 or natural language
});

if (sendError) {
  console.error(sendError);
  return;
}

Shortcut: Create and Send in One Call

Pass send: true on create to skip the separate send call:

const { data, error } = await resend.broadcasts.create({
  name: 'Flash Sale',
  from: 'Acme <[email protected]>',
  subject: 'Flash sale - 24 hours only',
  html: '<p>Shop now!</p>',
  segmentId: 'seg_abc123',
  send: true,
});

Get, List, Update, Cancel, Duplicate, Delete

// Get
const { data, error } = await resend.broadcasts.get('bc_abc123');

// List with pagination
const { data, error } = await resend.broadcasts.list({ limit: 10, offset: 0 });

// Update a draft
const { data, error } = await resend.broadcasts.update('bc_abc123', {
  subject: 'Updated subject line',
});

// Cancel a queued or scheduled broadcast — stops a queued send mid-flight, or
// reverts a scheduled one to draft. Does not remove the broadcast.
const { data, error } = await resend.broadcasts.cancel('bc_abc123');

// Duplicate — creates a new draft named "<name> (copy)" with the same content.
// Works on any broadcast, including sent ones. Returns the new broadcast's id.
const { data, error } = await resend.broadcasts.duplicate('bc_abc123');

// Delete — draft or scheduled only (deleting a scheduled broadcast also
// cancels its delivery). Sent broadcasts cannot be deleted.
const { data, error } = await resend.broadcasts.remove('bc_abc123');

// Clicked links — ranked by total clicks, paginated with cursors
const { data, error } = await resend.broadcasts.clickedLinks('bc_abc123', { limit: 10 });

Recipients

List who a broadcast was sent to, filtered by a single event type. Results are paginated with cursors (after / before).

// Who opened it
const { data, error } = await resend.broadcasts.recipients('bc_abc123', {
  type: 'opened',
});

// Who bounced, filtered to permanent bounces only
const { data, error } = await resend.broadcasts.recipients('bc_abc123', {
  type: 'bounced',
  bounceType: 'permanent',
});

type is required: sent, delivered, opened, clicked, bounced, complained, unsubscribed, or suppressed. Each recipient row always has id (an opaque pagination cursor, not a real entity id), contact_id (nullable), and email. Depending on type, rows also include count (opened/clicked), bounce_type (bounced), or clicked_links (clicked). bounce_type is only meaningful when type is bounced.

Python Example

import resend

resend.api_key = "re_xxxxxxxxx"

broadcast = resend.Broadcasts.create({
    "name": "March Newsletter",
    "from": "Acme <[email protected]>",
    "subject": "Your March update",
    "html": "<p>Hello!</p>",
    "segment_id": "seg_abc123",
})

resend.Broadcasts.send({"broadcast_id": broadcast["id"]})

Contact Property Interpolation

Use triple-mustache with a pipe for fallbacks: {{{PROPERTY_KEY|fallback}}}

<p>Hi {{{FIRST_NAME|there}}}, your balance is {{{BALANCE|0}}}.</p>
<a href="{{{RESEND_UNSUBSCRIBE_URL}}}">Unsubscribe</a>

Common Mistakes

Mistake Fix
Expecting create to send the broadcast create makes a draft. Call send separately, or pass send: true
Calling .delete() instead of .remove() Node.js SDK uses .remove() for all delete operations
Deleting a sent broadcast Only draft or scheduled broadcasts can be deleted
Cancelling a draft or sent broadcast Only queued or scheduled broadcasts can be cancelled
Using .remove() when you just want to stop delivery .cancel() stops/reverts without deleting the broadcast; .remove() deletes it entirely
Missing segmentId Required — broadcasts target segments, not all contacts
Missing unsubscribe link Include {{{RESEND_UNSUBSCRIBE_URL}}} in HTML
{{VAR}} instead of {{{VAR}}} Triple braces required for variable interpolation
Ignoring error return Node.js SDK returns { data, error } — always check error
scheduledAt format confusion Accepts both ISO 8601 (2025-03-15T10:00:00Z) and natural language (in 1 hour)
Treating clicked links' id as an entity ID It's an opaque pagination cursor for that row — use it with after/before, not to look up the link elsewhere
Passing bounceType with a non-bounced type Rejected with a 422 — only meaningful when type: 'bounced'