Transactional email — the stuff that actually matters — is a graveyard of bad APIs. SendGrid has been around since 2011, Postmark since 2010, Mailgun since 2011. They're all battle-tested and they all have the same problem: the API design is from 2010. Headers as nested objects, template variables as strings, webhook payloads that require three levels of parsing. Velocity X started with SendGrid, then switched to Resend in Q2 2026 because the DX difference is so stark that the migration paid for itself in two weeks of developer time. This piece explains what transactional email actually is, why Resend wins on ergonomics, how to set it up, and when you might still want to reach for Postmark instead.
What Is Transactional Email and Why Does It Matter
Transactional email is any message sent to a user as a direct result of their action. Lead notifications (sales gets pinged when a form submits), booking confirmations (user gets a calendar invite), password resets (user can recover their account), invoice reminders, deal-won Slack alerts — these are transactional. Unlike marketing email (batch, third-party, broadcast), transactional email is 1:1, immediate, and tied to a business process. If it doesn't arrive, your customer support gets the call.
Most teams start by throwing a SMTP server behind their app. Then deliverability tanks because residential IPs get spam-listed. So they buy an email SaaS. They pick SendGrid because it's the default. Six months later, they realize SendGrid's API is a mess and wish they'd picked something better. Velocity X learned that lesson the expensive way.
Why SendGrid (and Postmark, and Mailgun) Feel Antiquated
SendGrid's API is functional and reliable. But the ergonomics are painful. To send a simple email with a template and personalization, you write:
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail, To, From, Subject, HtmlContent, PlainTextContent
message = Mail(
from_email='noreply@velocity-x.com',
to_emails='contact@company.com',
subject='Lead notification',
html_content='...',
plain_text_content='...'
)
message.dynamic_template_id = 'd-abc123def'
message.personalization[0].dynamic_template_data = {
'lead_name': 'John',
'lead_email': 'john@example.com',
'company': 'Acme Corp'
}
sg = SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
response = sg.send(message)
For a simple email, that's 15 lines of ceremony. Mailgun and Postmark are better but still feel enterprise-y. And all three charge per contact or per message with overage, which adds accounting overhead for high-volume senders.
Enter Resend: The Modern Alternative
Resend launched in 2023 with a simple bet: the email API in 2023+ should feel like calling a modern HTTP service, not wrestling with SMTP abstractions. The API is one function call. To send the same message with Resend:
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
const data = await resend.emails.send({
from: 'noreply@velocity-x.com',
to: 'contact@company.com',
subject: 'Lead notification',
react: LeadNotificationEmail({
leadName: 'John',
leadEmail: 'john@example.com',
company: 'Acme Corp'
})
});
console.log(data.id); // immediately get a message ID
Four lines. The email itself is a React component. You get type safety on the props. You get instant feedback on whether the send succeeded. This is the difference between 2010 and 2025.
Three Things Resend Gets Right
1. React Email Components. Your transactional emails are React functions, not HTML templates stored in a dashboard. That means version control, type safety, preview during development, and no context-switching to a web UI to edit copy. The email is code. It lives in git. When you deploy, your emails deploy with it.
// LeadNotificationEmail.tsx
import { Html, Body, Section, Text } from 'react-email';
export function LeadNotificationEmail({
leadName,
leadEmail,
company,
}: {
leadName: string;
leadEmail: string;
company: string;
}) {
return (
New lead: {leadName}
Email: {leadEmail}
Company: {company}
);
}
2. Sub-100ms Delivery + Pricing That Doesn't Require an MBA. Resend's median send time is 50-90ms. Postmark is similar. SendGrid depends on your current load. More importantly, Resend charges $20 per 100,000 emails, full stop. No per-contact overage, no tiering, no "upgrade your plan" friction. You send 50k a month? You pay $10. You send 500k? You pay $100. The transparency is itself a feature.
3. Webhook Payloads That Don't Suck. When an email bounces, is marked spam, or is opened, Resend fires a webhook. The payload is clean JSON that maps to the email you sent. SendGrid's webhook payloads require you to parse CSV-like strings and cross-reference with stored metadata. Resend just gives you the email ID and event type. You wire it straight to your database.
Setup: DKIM, SPF, DMARC (30 Minutes)
Before you can send from your domain (e.g., noreply@velocity-x.com), you need to prove you own it. Resend walks you through adding three DNS records.
SPF (Sender Policy Framework) tells mail servers, "Yes, Resend is allowed to send on behalf of this domain." Add a TXT record pointing to Resend's servers.
DKIM (DomainKeys Identified Mail) cryptographically signs your emails so they can't be forged. Resend gives you a public key; you add it to DNS. When you send, Resend signs the message. Mail servers verify the signature matches your domain's public key.
DMARC (Domain-based Message Authentication) glues SPF and DKIM together and tells mail servers what to do if either fails (quarantine, reject, or just report). You add a policy like "reject if DKIM fails".
Take 15 minutes, add the three DNS records to your domain registrar, wait for propagation (usually instant, sometimes an hour), then in the Resend dashboard you'll see a green checkmark. You're live.
Resend vs The World: A Comparison Table
| Feature | Resend | Postmark | SendGrid | Mailgun |
|---|---|---|---|---|
| React Email Components | ✓ Native | ✗ Templates only | ✗ Templates only | ✗ Templates only |
| Pricing Model | $20/100k flat | $10–$100/mo + overage | $99+ tiered | $25+ tiered |
| Median Latency | 50–90ms | 50–100ms | 200–500ms | 200–400ms |
| Webhook API | ✓ Clean | ✓ Clean | ⚠ Verbose | ⚠ Verbose |
| TypeScript SDK | ✓ Excellent | ✓ Good | ✓ Good | ✓ Good |
| SMTP Fallback | ✗ API only | ✓ SMTP + API | ✓ SMTP + API | ✓ SMTP + API |
The Velocity X Setup: Lead Notifications → Resend → Slack
Here's how it works in practice. A lead fills out a form on a Velocity X site. The submission goes to a Netlify function. The function:
1. Stores the lead in Supabase (with RLS, obviously). 2. Fires a Resend email to the sales team with the lead summary. 3. Fires a Slack notification to the #sales channel. 4. Logs the event for analytics.
All three happen in parallel because none depends on the others succeeding. If Resend is slow (it won't be), Slack still fires. If Slack fails, the lead is still stored. Resend's response includes the message ID, which you log to Supabase for reconciliation later.
export const onRequest = async (context) => {
const { leadName, leadEmail, company } = await context.request.json();
const resend = new Resend(context.env.RESEND_API_KEY);
const { data, error } = await resend.emails.send({
from: 'leads@velocity-x.com',
to: 'sales@client.com',
subject: `New lead: ${leadName}`,
react: LeadNotificationEmail({ leadName, leadEmail, company })
});
if (error) {
console.error('Resend failed:', error);
}
// Also send to Slack
await fetch(context.env.SLACK_WEBHOOK, {
method: 'POST',
body: JSON.stringify({
text: `📬 New lead: ${leadName} from ${company}`
})
});
return new Response(JSON.stringify({ success: true, messageId: data?.id }));
};
Six Things You Should Know
What if I need to send from multiple domains?
Resend supports multiple sender identities. Add your domain once, then use any email address on that domain (noreply@, support@, billing@, etc.). Each one gets its own DKIM record.
Can I track opens and clicks?
Yes. Resend automatically inserts a tracking pixel for opens and wraps links with click-tracking redirects. You can disable it per email if you want. The webhook fires every time someone opens or clicks, so you can stitch it back to your contact and build re-engagement sequences on top (see our previous post on email remarketing flows).
What happens to test sends?
Use the `preview: true` flag to generate a preview link without actually sending. Perfect for QA. Resend also gives you a sandbox mode where you can test without hitting real inboxes.
Do I still need SMTP?
Not for transactional email. Resend's HTTP API is faster and more reliable than SMTP. If you have legacy code that's hardwired to SMTP, Postmark and SendGrid both offer SMTP, but I'd migrate rather than keep the old path alive.
How do I handle bounces and complaints?
Resend webhook events include "bounced", "complained", and "failed". Wire those webhooks to a function that marks the contact as undeliverable in your database. Next time you try to send to that address, you'll skip it automatically. This prevents your domain from getting spam-listed by repeatedly mailing dead addresses.
Can I send at scale without hitting rate limits?
Resend doesn't publish explicit rate limits (in our testing, we've sent 50k/hour without issues). Postmark is explicit: 50 req/sec per account, 300 req/sec across your team account. SendGrid's limits depend on your plan. For typical transactional workloads (under 100k/day), you'll never hit them on any provider.
The Bottom Line
Transactional email is a solved problem from a reliability standpoint — all four of these services will get your message delivered. The question is whether you want to spend mental energy fighting an API from 2010 or whether you want to write code that's clean, type-safe, and version-controlled. Resend wins on DX. For most new builds, it should be your default. For shops with heavy SMTP usage or workflows that demand maximum flexibility, Postmark and SendGrid are solid fallbacks. Velocity X picked Resend for the React Email ergonomics and the flat pricing. Two months in, we've never looked back. Start your next project here and you'll understand immediately why.