How to switch from Resend in 2 minutes (and keep your code)
Short answer: Point Resend's SDK at SendFox. Set RESEND_BASE_URL to https://api.sendfox.com/transactional/resend and RESEND_API_KEY to your SendFox key, verify your domain, and deploy. Your resend.emails.send() calls don't change.
Why switch? Mostly price: transactional email is included on every SendFox plan. Paid plans start at $19 a month for 100,000 sends shared with your campaigns, Resend Pro is $20 for 50,000, and SendFox's free plan covers 3,000 a month with no daily cap. (Full breakdown in Resend pricing explained.) Some teams also want a second provider they can fail over to without maintaining two integrations. Since your code doesn't change, you can try it on staging in an afternoon.
Before you start: what carries over
| Resend feature | On SendFox |
|---|---|
resend.emails.send() | Works as is |
resend.batch.send() | Works as is, up to 100 emails a call |
resend.emails.get() | Works as is |
to, cc, bcc as string or array | Both accepted: up to 10 addresses per email across to, cc and bcc, at most 2 in bcc (Resend allows 50) |
tags | The first tag's value becomes the SendFox tag |
Webhooks and webhooks.verify() | Works with the Resend format and your SendFox secret |
| Idempotency keys | Supported for 24 hours |
attachments | Works with filename and content, up to 10 files and 5 MB. path and inline content_id are refused with a 422 |
scheduled_at | Not yet. Refused with a 422, never sent without it |
Step 1: Get a SendFox API key
Sign up free and open Transactional. Your first API key is created when the page loads, and the code snippets on that page are already filled in with it. Copy it into your secrets manager: the full key stays visible on that page for an hour, then only its name is shown.
Step 2: Verify your sending domain
Add the domain you send from (for example mail.yourapp.com) and paste the DNS records SendFox shows into your DNS provider. They're the same kind of DKIM and SPF records Resend asked you for. Most DNS providers pick them up in a few minutes.
Until the domain is verified you can still test: the sandbox sender can email your own address and SendFox's test addresses.
Step 3: Change two environment variables
RESEND_BASE_URL=https://api.sendfox.com/transactional/resend
RESEND_API_KEY=<your SendFox transactional key>
Resend's Node SDK reads RESEND_BASE_URL on its own. If your SDK doesn't, pass the same URL as the base URL option when you create the client. Your send code stays exactly as it was:
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({
from: 'Acme <[email protected]>',
to: '[email protected]',
subject: 'Your login code',
html: '<p>Your code is 123456</p>',
tags: [{ name: 'category', value: 'login_code' }],
});
Message ids now start with tm_. If you store ids, make sure the column takes that shape.
Step 4: Switch your webhook
Under Transactional > Webhooks, add the same URL your Resend webhook posts to and choose the Resend format. Events arrive as email.sent, email.delivered, email.delivery_delayed, email.bounced, email.complained and email.failed, signed with the same svix-* headers. Replace the signing secret in your environment with the SendFox one and webhooks.verify() keeps passing.
Failed deliveries are retried after 1 minute, 15 minutes, 2 hours and 18 hours, with the same event id, so dedupe on it.
Step 5: Send a test and watch the feed
Send to [email protected], [email protected] or [email protected] to see each outcome, including the webhook, without touching a real inbox. Test addresses never count against your quota. The live feed on the Transactional page shows every message and its event timeline, and rejected requests get their own log with the reason.
Things to know after you switch
- Quota counts recipients, same as Resend: every
to,ccandbccaddress is one email. - Rate limit: 5 requests a second. A 429 carries a
Retry-Afterheader to back off by. - No overage. Emails count toward your plan's monthly sends, the same pool your campaigns use. Past the cap you get a 429 (
monthly_quota_exceeded, withremainingandrequestedin the body) until you upgrade, so nothing bills unless you choose to. See every plan. - Open and click tracking is per tag, and it's always off for login codes, magic links and password resets, because mailbox scanners click those links and burn the token.
- Errors tell you the fix. Every error response has a
fixfield that says what to change.
Full reference: Transactional API docs, also as one Markdown page for AI coding agents. Product overview: the SendFox email API. Still deciding? Compare the best Resend alternatives.
FAQ
Do I need to change my code to switch from Resend?
No. SendFox accepts Resend's request shape at /transactional/resend. resend.emails.send, resend.batch.send and resend.emails.get keep working once RESEND_BASE_URL and RESEND_API_KEY point at SendFox.
Will my Resend webhook handler still work?
Yes. Pick the Resend format for your SendFox webhook and the events arrive with the same types (email.delivered, email.bounced and so on) and the same svix signature headers. Swap in the SendFox signing secret.
What does not carry over?
Scheduled sends, inline images (content_id) and attachments fetched from a URL (path) are not supported yet. Requests that use them get a 422 validation error instead of being sent without them, so you will see it in testing. Regular attachments with base64 content work.
Can I move back to Resend?
Yes. Your code never changed, so moving back is the same two variables in reverse.
Send your first email in two minutes.
Included on every SendFox plan. Free for 3,000 emails a month, paid plans from $19 a month for 100,000 sends. Already on Resend? Change two environment variables.
Get your free API key