Official Convex + Resend integration with queueing, batching, idempotency, and webhook-driven delivery tracking.
Install
npx skillscat add alex-jordan547/agent-setup/convex-dev-resend Install via the SkillsCat registry.
SKILL.md
Convex + Resend
Use the official @convex-dev/resend component to send transactional emails from Convex with queueing, batching, retries, and delivery tracking.
When to Use
- The user wants resilient email delivery from Convex functions.
- The user needs non-blocking bulk sends that respect Resend rate limits.
- The user asks for webhook-based status tracking (
delivered,bounced,opened, etc.). - The user needs idempotent retries to prevent duplicate emails.
Core Workflow
- Install and register the component in
convex/convex.config.ts. - Instantiate
Resendonce in a Convex module (for exampleconvex/email/send.ts). - Send emails via
resend.sendEmail(ctx, payload)from mutations/actions. - Mount webhook endpoint in
convex/http.tsand setRESEND_WEBHOOK_SECRET. - Add cleanup cron for old finalized emails.
- For production, set
testMode: falseexplicitly.
Setup
npm install @convex-dev/resend// convex/convex.config.ts
import { defineApp } from 'convex/server';
import resend from '@convex-dev/resend/convex.config.js';
const app = defineApp();
app.use(resend);
export default app;Copy-Paste Example
// convex/email/send.ts
import { v } from 'convex/values';
import { Resend } from '@convex-dev/resend';
import { components } from '../_generated/api';
import { internalAction } from '../_generated/server';
export const resend = new Resend(components.resend, {
// Keep true in dev/sandbox, set to false in production.
testMode: true,
});
export const sendWelcomeEmail = internalAction({
args: {
to: v.string(),
username: v.string(),
},
returns: v.null(),
handler: async (ctx, args) => {
await resend.sendEmail(ctx, {
from: 'CEL <onboarding@resend.dev>',
to: args.to,
subject: 'Bienvenue sur CEL',
html: `<p>Salut ${args.username}, bienvenue.</p>`,
});
return null;
},
});Webhook Wiring
// convex/http.ts
import { httpRouter } from 'convex/server';
import { httpAction } from './_generated/server';
import { resend } from './email/send';
const http = httpRouter();
http.route({
path: '/resend-webhook',
method: 'POST',
handler: httpAction(async (ctx, req) => {
return await resend.handleResendEventWebhook(ctx, req);
}),
});
export default http;Set these environment variables in Convex deployment:
RESEND_API_KEYRESEND_WEBHOOK_SECRET
Production Checklist
- Set
testMode: falsebefore sending to real recipients. - Verify domain/sender in Resend dashboard.
- Configure webhook with
email.*events. - Track failures with
resend.status(ctx, emailId). - Add periodic cleanup via
components.resend.lib.cleanupOldEmails.
Troubleshooting
- 400/401 from Resend: Validate
RESEND_API_KEYin Convex environment. - Webhook signature errors: Check
RESEND_WEBHOOK_SECRETand endpoint URL. - No emails to real inboxes: Ensure
testModeis disabled. - Need attachments/custom features: Use
sendEmailManuallywith Resend SDK.
Limitations
sendEmailfollows Resend batch API constraints.- Attachments and niche options may require
sendEmailManually. - React Email rendering requires a Convex Node action (
'use node';). - This skill does not manage DNS/domain verification in Resend.
Related Skills
convex-best-practicesemail-systemsreact-email