DocsComing soon

Migrate from Resend

letr.dev as a Resend alternative, with API keys, DNS records, webhook events, SMTP settings and suppressions mapped for the move.

For an app that sends its sign-in codes, receipts and password links through Resend. letr.dev takes over that part. It sends no newsletters or campaigns and stores no templates.

letr.dev costs CHF 12 a month for 10'000 recipients, then CHF 0.50 per started 1'000 recipients, up to a monthly ceiling of 10'000 or 25'000 recipients that you choose; for more, letr.dev can grant a higher ceiling (limits). letr.dev counts per recipient: an email to three people counts three (overage).

Steps

  1. Sign up. The account goes live on payment and starts on step 0 with 50 recipients a day (daily limit).
  2. Add your sending domain and publish letr.dev's DKIM record next to Resend's records.
  3. Create one API key per app.
  4. Create a webhook endpoint for the events you use.
  5. Import your Resend suppressions.
  6. Point the app at letr.dev's API or SMTP relay.
  7. Remove Resend's records once Resend sends nothing more from the domain.

API keys

Resend letr.dev
full_access sending_access, the least privilege an app that sends needs; full_access only for the one-off suppression import
sending_access, optionally limited to a domain sending_access, optionally limited to some of your domains
None read_only: every GET under /v1

Source: Resend's create API key reference, 01.11.2026; letr.dev's permissions.

DNS records

letr.dev needs one TXT record for DKIM at letr._domainkey, and advises DMARC. Bounces return to bounces.letr.dev, so your SPF record stays as it is (DNS records). Resend's records sit on the return-path subdomain, send by default; names below are relative to your domain.

Resend record With letr.dev
MX send, to feedback-smtp.us-east-1.amazonses.com (the region part varies) Not needed
TXT send, v=spf1 include:amazonses.com ~all Not needed
CNAME records on the return-path subdomain and its r sibling, where Resend shows CNAMEs instead Not needed
TXT resend._domainkey (DKIM) Replaced by TXT letr._domainkey
TXT _dmarc Keep it: a record that starts with v=DMARC1 counts

Because the selectors differ, both DKIM records can stay side by side during the switch. Remove Resend's once its last emails are out. Source: Resend's add a domain, Cloudflare and custom return path guides, 01.11.2026.

Webhook events

Most event names are the same.

Resend event letr.dev event
email.sent email.sent, when letr.dev's mail server accepted the email for delivery; at Resend, when the API request succeeded
email.delivered email.delivered
email.delivery_delayed email.deferred
email.bounced email.bounced
email.complained email.complained
email.failed email.failed; key, domain and quota errors letr.dev answers at once in the API response instead
email.suppressed None: letr.dev refuses at once to send to a suppressed address, with status 422
email.opened, email.clicked None: letr.dev does not track opens or clicks
email.scheduled None: letr.dev sends scheduled emails without an event for scheduling
email.received, suppression.*, contact.*, domain.* and topic events None

letr.dev signs every event with the endpoint's own secret; verify the signature before you trust it. Source: Resend's event types, 01.11.2026.

SMTP

Resend uses the host smtp.resend.com on port 465 or 2465 with implicit TLS, or 25, 587 or 2587 with STARTTLS, the username resend and an API key. letr.dev uses:

Setting Value
Host smtp.letr.dev
Port 465 with implicit TLS or 587 with STARTTLS (required)
Username letr
Password An API key with sending_access

The SMTP guide shows the settings for each framework. Source: Resend's SMTP guide, 01.11.2026.

Suppressions

On Resend's Suppressions page, Download CSV exports your suppressions, whether they came from a bounce, a complaint or your own entry (suppressions, 01.11.2026). letr.dev reads only the first column of each line. Make sure the email address is the first column, then create a full_access key in the dashboard only for the import and import the file with it:

curl https://api.letr.dev/v1/suppressions/import \
  -H "Authorization: Bearer $LETR_API_KEY" \
  -H "Content-Type: text/csv" \
  --data-binary @suppressions.csv

An import holds at most 10'000 lines and 1 MB; split larger files. Imported addresses get the reason manual (import a list). When the import is done, revoke that key in the dashboard; revoking stops it at once. The app keeps its sending_access key.

What letr.dev does not do

  • Newsletters and campaigns: letr.dev sends transactional email only.
  • Templates: your app renders the email and sends the finished html and text.
  • Receiving email, contacts and topics.
  • Open and click tracking.