Webhooks y Eventos
Cuando algo pasa con un email que enviaste (se entregó, se abrió, rebotó), Resend puede notificarte en tiempo real enviando un POST request a una URL que tú configuras. Eso es un webhook: una llamada HTTP automática que tu servidor recibe cada vez que ocurre un evento.
Eventos Disponibles
Resend puede notificarte sobre los siguientes eventos:
| Evento | Descripción |
|---|---|
email.sent | El email fue aceptado por Resend y está en cola para envío |
email.delivered | El email fue entregado exitosamente al servidor del destinatario |
email.delivery_delayed | La entrega del email se retrasó (el servidor destino tiene problemas temporales) |
email.bounced | El email rebotó -- la dirección no existe, el buzón está lleno, u otro error permanente |
email.complained | El destinatario marcó tu email como spam |
email.opened | El destinatario abrió el email (detectado vía tracking pixel) |
email.clicked | El destinatario hizo clic en un link dentro del email |
Configurar un Webhook en el Dashboard
- En el dashboard de Resend, ve a Webhooks en el menú lateral
- Haz clic en Add Endpoint
- Ingresa la URL de tu endpoint (por ejemplo
https://tuapp.com/api/webhooks/resend) - Selecciona los eventos a los que quieres suscribirte
- Guarda la configuración y copia el signing secret -- lo necesitas para verificar las firmas
Crear el Endpoint en NextJS
Este es un Route Handler básico que recibe y procesa los eventos de Resend:
// app/api/webhooks/resend/route.ts
import { NextResponse } from 'next/server'
export async function POST(request: Request) {
const payload = await request.json()
switch (payload.type) {
case 'email.delivered':
console.log('Email entregado:', payload.data.email_id)
break
case 'email.bounced':
console.log('Email rebotó:', payload.data.email_id)
// Marcar email como inválido en tu base de datos
break
case 'email.complained':
console.log('Marcado como spam:', payload.data.email_id)
// Desuscribir automáticamente
break
}
return NextResponse.json({ received: true })
}Verificar la Firma del Webhook
El endpoint anterior funciona, pero tiene un problema de seguridad: cualquiera que conozca la URL puede enviar requests falsos. Para evitarlo, verifica la firma que Resend incluye en cada webhook.
Resend usa Svix para manejar webhooks. Svix firma cada request con un secret que solo tú y Resend conocen. Para verificar la firma, instala el paquete de Svix:
npm install svixLuego actualiza tu endpoint para verificar antes de procesar:
// app/api/webhooks/resend/route.ts
import { Webhook } from 'svix'
const wh = new Webhook(process.env.RESEND_WEBHOOK_SECRET!)
export async function POST(request: Request) {
const body = await request.text()
const headers = {
'svix-id': request.headers.get('svix-id')!,
'svix-timestamp': request.headers.get('svix-timestamp')!,
'svix-signature': request.headers.get('svix-signature')!,
}
try {
const payload = wh.verify(body, headers)
// Procesar el evento verificado
return new Response('OK', { status: 200 })
} catch {
return new Response('Firma inválida', { status: 401 })
}
}Verifica siempre la firma en producción
Sin verificación de firma, cualquiera puede enviar requests falsos a tu endpoint y disparar acciones no autorizadas en tu aplicación. En desarrollo local puedes omitirlo para pruebas rápidas, pero en producción es obligatorio.
Reintentos automáticos
Resend usa Svix para webhooks, que garantiza at-least-once delivery (entrega al menos una vez) con reintentos automáticos. Si tu endpoint devuelve un error (status 5xx), Svix reintentará el envío con backoff exponencial durante varias horas. Esto significa que tu endpoint puede recibir el mismo evento más de una vez -- diseña tu lógica para que sea idempotente.
Testing en Local
Tu endpoint en localhost no es accesible desde internet, así que Resend no puede enviar webhooks a tu máquina directamente. Tienes dos opciones para probar en desarrollo:
Opción 1: ngrok
Expone tu localhost con una URL pública temporal:
ngrok http 3000Copia la URL que te da ngrok (algo como https://abc123.ngrok.io) y configúrala como endpoint del webhook en el dashboard de Resend.
Opción 2: Svix CLI
Svix ofrece un CLI que te permite recibir webhooks directamente en tu máquina:
svix listen http://localhost:3000/api/webhooks/resendAmbas opciones funcionan bien para desarrollo. Elige la que te resulte más cómoda.