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, max 50 each)
Subjectsubjectsubject
Bodyhtml / texthtml / text (at least one)
Reply-toreply_toreply_to (array, 1–25)
Custom headersheadersheaders (string → string object)
Filterable labelstags{name, value} pairstags{name, value} pairs, max 20
Round-trip context— (tags double as context)metadata — arbitrary JSON, max 2 KB
Open/click trackingper-domain dashboard settingtrack_opens / track_clicks (default true)
Categorycategory: transactional (default) or marketing
Porting notes:
  • Tags keep their shape, and metadata is an upgrade. Resend tags are the same {name, value} pairs Bird uses, but they carry value constraints that pushed correlation data into tag values; on Bird, move correlation context into metadata (arbitrary JSON, read back via GET /v1/email/messages/{email_id}) and keep tags for filtering.
  • 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).
  • attachments and scheduled_at are reserved on Bird and return 422 unsupported_feature today — if you use either on Resend, plan around the gap before cutover. The same goes 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.
  • 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
Unsubscribeemail.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 that 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.