Docs

Integrating

Migrating from Resend

What is identical, what the error names map onto, and the two things that behave differently on purpose.

The request shape is the same

POST /emails takes from, to, subject, html and text with the same names and the same snake_case on the wire. A working Resend call keeps working when the base URL and the key change. The full shape is on the POST /emails page.

Error names resolve

Resend's error names are accepted as aliases for our codes, so a find-replace migration does not have to rewrite every branch on the way in. The table is published in openapi.json under x-resend-error-aliases, generated from the same module the API resolves with (packages/core/src/error-catalog.ts), and every one of our 71 codes is in the error catalogue.

What our errors add is fix: a sentence naming the endpoint to call next. Code that branches on code keeps working; code that shows the user a message gets a better one for free.

Two differences, both deliberate

Region is optional and defaults to `us` (Oregon). Pass eu for Helsinki. See Domains and DNS. Tracking clicks and opens uses a host on *your* domain, never a shared one.

Keys have budgets, and the budget is enforced. A key with a ceiling stops at the ceiling rather than continuing and invoicing you. If you port a key that sends heavily, set its budget to match your real volume before you cut over, or the first thing the migration does is stop.

What you gain in the move

Order of operations

  1. Add and verify your domains here while the old provider still sends.
  2. Create keys with budgets, one per sender, and set up webhooks.
  3. Move a low-volume stream first and watch the reputation panel for a few days.
  4. Move the rest, and keep the old provider's keys alive until you have a week of clean data.