GlassKit
Add-ons

Email (Resend)

The Email add-on wires the @convex-dev/resend component for transactional sends: welcome email on sign-up plus any custom email your product needs.

The Email add-on registers the official @convex-dev/resend Convex component. The component handles queueing, batching, rate limiting, and durable delivery. Your code calls resend.sendEmail(ctx, ...) and the component takes care of the rest.

What ships in this add-on:

  • packages/backend/convex/email.ts: exports the resend instance plus an internal sendWelcomeEmail mutation
  • A scheduled call to sendWelcomeEmail from users.upsertFromClerk on first sign-up
  • The Resend delivery-status webhook route in http.ts
  • The RESEND_API_KEY + RESEND_WEBHOOK_SECRET + EMAIL_FROM env declarations in env.ts

Enable

Flip the files + insert fence content

pnpm run addon enable email

This single command:

  • renames packages/backend/convex/email.ts.disabledemail.ts
  • inserts the Resend imports + app.use(resend) in convex.config.ts
  • inserts the Resend webhook route in http.ts
  • inserts the welcome-email scheduler call in users.ts
  • inserts the RESEND_API_KEY / RESEND_WEBHOOK_SECRET / EMAIL_FROM schema in env.ts

It then prints the Convex env keys you need to set.

Grab the keys + push them to Convex

In the Resend dashboard:

  • API key (re_…): Resend → API keys → Create
  • Webhook endpoint → Resend → Webhooks → Add Endpoint, URL is https://<your-deployment>.convex.site/resend-webhook. Copy the signing secret (whsec_…).
  • Verified sender: for testing use Resend's onboarding sender (onboarding@resend.dev); for production verify your own domain and use "Your App <hello@your-domain.com>".

Push them in:

cd packages/backend
pnpm exec convex env set RESEND_API_KEY re_...
pnpm exec convex env set RESEND_WEBHOOK_SECRET whsec_...
pnpm exec convex env set EMAIL_FROM "Your App <hello@your-domain.com>"
pnpm exec convex dev --once    # regen _generated/api.d.ts

Your AI coder can drive this conversationally; see the root AGENTS.md for the exact prompt order.

Verify

Sign up a new test user on the companion. Within seconds, the welcome email should appear in the inbox. Resend's dashboard shows delivery status; the webhook populates the component's emails table for status queries.

Sending custom emails

Anywhere in a Convex mutation / action:

import { resend } from "./email";

await resend.sendEmail(ctx, {
  from: "Your App <hello@your-domain.com>",
  to: ["user@example.com"],
  subject: "Your report is ready",
  html: "<p>Hi — your weekly report is attached.</p>",
});

The component queues, batches, and retries automatically.

Disable

pnpm run addon disable email

Renames email.ts back to .disabled and clears all fenced regions. Then drop the three env keys:

cd packages/backend
pnpm exec convex env remove RESEND_API_KEY
pnpm exec convex env remove RESEND_WEBHOOK_SECRET
pnpm exec convex env remove EMAIL_FROM
pnpm exec convex dev --once

Component-owned tables (delivery history) stay in Convex: no data loss on re-enable.

Going live

Same flow as the rest of the boilerplate's "Going to production" story:

  1. Verify a sender domain in Resend (you've been on the onboarding sender for testing; switch to your own domain).
  2. Set EMAIL_FROM in your production Convex env to your verified sender ("Your App <hello@yourdomain.com>").
  3. Create a separate webhook endpoint pointed at your production .convex.site URL. Resend allows multiple endpoints per account.

Troubleshooting

Welcome email never arrives

Most common: your verified sender hasn't been set up in Resend yet, so they're rejecting your sends. Check Resend → Emails for the delivery status. If they show pending indefinitely, your sender domain isn't verified.

Webhook signature verification fails

RESEND_WEBHOOK_SECRET in the Convex env doesn't match the endpoint's signing secret. Reveal the secret in Resend → Webhooks → your endpoint, paste it into Convex.

On this page