Resend
Resend is the email API built for developers. Use it in your Astro project to send transactional emails. Welcome messages, contact form replies, order confirmations, and more. No SMTP config, no deliverability headaches.
Last updated: 2026-03-29
Common Email Flows
Resend handles transactional emails in your Astro project. The two most common flows are a welcome email on user sign-up, and a contact form confirmation when a visitor submits a message.
User signs up
│
├── POST /api/auth/register
│ → create user in database
│ → resend.emails.send() → "Welcome aboard!"
│ email contains: getting started tips + dashboard link
│
└── User lands on dashboard
Contact form submitted
│
├── POST /api/contact
│ → validate form fields
│ → resend.emails.send() → confirmation to visitor
│ → resend.emails.send() → notification to site owner
│
└── Visitor sees success messageSetup & Configuration
Create a free Resend account, verify your sending domain, and generate an API key. Resend's free tier covers 3,000 emails/month. Enough for a growing template business. Add the key and sender details to your environment variables.
# Install Resend
npm install resend
# .env
RESEND_API_KEY=re_your_api_key
FROM_EMAIL=Flux Themes <hello@yourdomain.com>
SITE_URL=https://yourdomain.com
// src/lib/resend.js
import { Resend } from 'resend';
export const resend = process.env.RESEND_API_KEY
? new Resend(process.env.RESEND_API_KEY)
: null;Magic Link Email (Access Flow)
When a buyer enters their email on the access page, the API checks Supabase for a confirmed purchase, generates a secure token, and sends a branded HTML email with a one-click login link. The token expires after 24 hours.
// src/pages/api/auth/request-link.js
import { Resend } from 'resend';
import { createClient } from '@supabase/supabase-js';
import crypto from 'crypto';
const resend = new Resend(process.env.RESEND_API_KEY);
const supabase = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY
);
export async function POST({ request }) {
const { email } = await request.json();
// 1. Verify the buyer has a confirmed purchase
const { data: purchases } = await supabase
.from('purchases')
.select('id')
.ilike('email', email)
.eq('status', 'confirmed')
.limit(1);
if (!purchases?.length) {
return new Response(JSON.stringify({ error: 'No purchase found' }), { status: 404 });
}
// 2. Generate token and store in Supabase
const token = crypto.randomBytes(32).toString('hex');
const expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString();
await supabase.from('auth_tokens').insert({
email, token, token_type: 'magic_link', expires_at: expiresAt
});
// 3. Send email via Resend
const loginUrl = `${process.env.SITE_URL}/buyer/${token}`;
await resend.emails.send({
from: process.env.FROM_EMAIL,
to: email,
subject: 'Your Templates Are Ready! 🎉',
html: `<a href="${loginUrl}">Open My Templates →</a>` // use your full HTML template
});
return new Response(JSON.stringify({ success: true }), { status: 200 });
}Update Notification Email (GitHub Webhook)
Every time you push to the template GitHub repo, a webhook fires to your API. Flux verifies the signature, looks up all confirmed buyers in Supabase, and sends each one an update email with the latest commit summary.
// src/pages/api/webhooks/github.js
import crypto from 'crypto';
import { Resend } from 'resend';
const resend = new Resend(process.env.RESEND_API_KEY);
export async function POST({ request }) {
const payload = await request.text();
// 1. Verify GitHub signature
const sig = request.headers.get('x-hub-signature-256');
const expected = crypto
.createHmac('sha256', process.env.GITHUB_WEBHOOK_SECRET)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(sig.replace('sha256=', ''), 'hex'), Buffer.from(expected, 'hex'))) {
return new Response('Unauthorized', { status: 401 });
}
const { repository, commits } = JSON.parse(payload);
// 2. Fetch all buyers for this template from Supabase
const { data: purchases } = await supabase
.from('purchases')
.select('email')
.eq('status', 'confirmed')
.ilike('product_name', repository.name.replace(/[-_]/g, ' '));
// 3. Send update notification to each buyer
for (const { email } of purchases) {
await resend.emails.send({
from: process.env.FROM_EMAIL,
to: email,
subject: `🚀 ${repository.name} — New Updates Available`,
html: `<p>New commit: ${commits[0].message}</p>
<code>git pull origin main</code>`
});
}
return new Response(JSON.stringify({ success: true }), { status: 200 });
}