Documentation
Sign inGet started

Migrate from Resend

The provider-specific half of the migration guide: how Resend's POST /emails payload, suppression handling, and Svix-signed webhooks map onto Bird. Do the steps in the main guide in order; this page is the lookup table for steps 1, 3, and 4. Of the providers covered, this is the shortest port: the payloads are nearly field-for-field identical.

Map the send call

What it doesResendBird
Senderfromfrom
Recipientsto / cc / bcc (max 50)to / cc / bcc (arrays)
Subjectsubjectsubject
Bodyhtml / texthtml / text (at least one)
Reply-toreply_toreply_to (array)
Custom headersheadersheaders (string → string object)
Filterable labelstags: {name, value} pairstags: {name, value} pairs
Round-trip context(none; tags double as context)metadata: arbitrary JSON
Schedulingscheduled_atscheduled_at
Open/click trackingper-domain dashboard settingtrack_opens / track_clicks (default true)
Category(none)category: marketing (default) or transactional
Bird-side field caps and defaults (recipient counts, tag and metadata limits) live in Sending email.
Porting notes:
  • Tags keep their shape, and metadata is an upgrade. Resend tags are the same {name, value} pairs Bird uses, but their value constraints pushed correlation data into tag values. On Bird, move correlation context into metadata (arbitrary JSON, echoed back on every webhook event and returned on API reads) and keep tags for filtering. See tags vs metadata.
  • Tracking moves into the payload. Resend toggles open/click tracking per domain in the dashboard; Bird sets track_opens/track_clicks per message (both default true).
  • scheduled_at maps directly, name and all; see scheduled sending. For react, render your React Email templates to HTML in your application (the render function from @react-email/render works unchanged) and send the result as html.
  • Attachments port directly. Resend's attachments (base64 content) map to Bird's attachments array; set content_id for inline images.
  • Batch sending ports directly. Resend's POST /emails/batch becomes Bird's batch endpoint, with per-entry results in both cases.

Export suppressions

Resend doesn't expose a dedicated suppression-list export. Pull the addresses whose last event is bounced or complained, from the Emails view in the dashboard or by walking your stored webhook events if you've been recording them, and run the list through the import loop. If you use Audiences for marketing mail, also carry over contacts marked unsubscribed.

Translate webhook events

OutcomeResendBird
Accepted/processedemail.sentemail.acceptedemail.processed
Deliveredemail.deliveredemail.delivered
Temporary failureemail.delivery_delayedemail.deferred
Permanent bounceemail.bouncedemail.bounced / email.out_of_band_bounce
Spam complaintemail.complainedemail.complained
Blocked/suppressedemail.failedemail.rejected
Openemail.openedemail.opened
Clickemail.clickedemail.clicked
Unsubscribe(none)email.list_unsubscribed
The signing scheme barely changes: Resend delivers through Svix (svix-id, svix-timestamp, svix-signature headers), and Bird signs per the Standard Webhooks specification, which uses the same HMAC construction with webhook-* header names. If you verify Resend deliveries today, the same code verifies Bird's after renaming the three headers; or use the recipe in Webhooks & events.
One behavioral difference: Resend's events are message-scoped; Bird's delivery events are recipient-scoped (recipient_id alongside email_id), so a three-recipient send produces three delivery outcomes, not one.

Cut over

Work through domains & DNS and the sandbox smoke test in the main guide; both are provider-independent.

Next steps

  • Sending domains: registration, verification lifecycle, and the DNS records you're re-pointing
  • Webhooks & events: endpoint setup and Standard Webhooks verification
  • Testing sandbox: smoke-test the new integration before cutover
  • Suppressions: confirm your imported list and how Bird maintains it from here