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.

Terminal
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 message

Setup & 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.

src/lib/resend.js
# 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;

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
// 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 });
}