Migrar de Resend a Postal
La API de Postal usa los mismos recursos, nombres de campos y formatos de respuesta que la API de Resend. En la mayoría de los casos, migrar es cambiar la URL base y la clave, y el SDK de Resend sigue funcionando.
- Qué cambia en el código
- URL y clave
- Se pueden ejecutar las dos a la vez
- Sí
- Tiempo estimado, más el DNS
- Unas horas
- Tipo de envío
- API y SMTP
Verifica el dominio en Postal
Registra el dominio en el panel y crea los registros de DNS que muestra. Puedes mantener el mismo dominio en Resend y en Postal a la vez: los registros son diferentes, porque Postal usa el selector postal en el DKIM.
- Postal comprueba los registros por sí sola durante hasta 72 horas y avisa por webhook (
domain.updated) - Si el dominio aún no tiene DMARC, los grandes proveedores se lo exigen a quien envía volumen: empieza con
p=noney endurece después - Crea una clave de API en Claves de API. Para sistemas que solo envían, usa una clave solo de envío, restringida al dominio
Apunta el SDK a Postal
Los SDK oficiales de Resend funcionan apuntando a Postal. En Node.js, pasa baseUrl al crear el cliente o define RESEND_BASE_URL. El cuerpo del correo, la respuesta ({"id"}) y el formato de error (statusCode, name, message) siguen igual.
Con el SDK
- const resend = new Resend(process.env.RESEND_API_KEY);+ const resend = new Resend(process.env.POSTAL_API_KEY, { baseUrl: 'https://api.postal.so' });O solo con el entorno
# Sin tocar el código que ya usa new Resend(clave)
RESEND_BASE_URL=https://api.postal.so
RESEND_API_KEY=ps_sua_chavePor HTTP
- curl -X POST 'https://api.resend.com/emails' -H 'Authorization: Bearer re_...'+ curl -X POST 'https://api.postal.so/emails' -H 'Authorization: Bearer ps_...'Por SMTP
- Servidor: smtp.resend.com- Usuario: resend- Contraseña: re_...+ Servidor: smtp.postal.so+ Usuario: postal (resend también se acepta)+ Contraseña: ps_...Probado con el paquete resend 6.34 (Node.js): envío simple, programado, por lotes y con idempotencia, consulta, edición y cancelación, adjuntos, dominios, claves de API, webhooks y webhooks.verify. Otros SDK que permiten cambiar la URL base usan el mismo formato, pero no se han probado uno a uno: si encuentras una diferencia, avísanos por el formulario de contacto.
Importa lo que ya tienes
Antes de cambiar el tráfico, lleva la lista de supresión, para no escribir a quien ya pidió salir, y los contactos, si usas la audiencia. Recrea también las plantillas: las variables {{{VAR}}} son las mismas que en Resend.
- Lista de supresión:
POST /suppressions/batch/add, con 100 direcciones por solicitud - Contactos: importación por CSV, desde el panel o por la API
- Plantillas: crea y publica con el mismo alias y las mismas variables; los envíos usan siempre la última versión publicada
curl -X POST https://api.postal.so/suppressions/batch/add \
-H "Authorization: Bearer $POSTAL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"emails": ["[email protected]", "[email protected]"]}'Recrea el webhook y comprueba la firma
Los nombres de los eventos son los mismos (email.delivered, email.bounced…), con el formato {type, created_at, data}. Lo que cambia es el secreto: en Postal, cada endpoint tiene su signing_secret (whsec_…). La firma sigue el estándar Standard Webhooks, y resend.webhooks.verify acepta los encabezados webhook-* y svix-*.
- Crea el endpoint en Webhooks (panel o
POST /webhooks) y elige los eventos - Envía un evento de prueba desde el panel antes de cambiar el tráfico
- Son 8 intentos en unas 27 horas, siempre con el mismo
webhook-id; el reenvío manual se hace desde el panel o conPOST /webhooks/{id}/events/{event_id}/replay - Otros lenguajes: consulta la verificación de la firma
import { Resend } from 'resend';
const resend = new Resend(process.env.POSTAL_API_KEY, { baseUrl: 'https://api.postal.so' });
// Lee el cuerpo crudo de la solicitud, sin reformatear el JSON
const event = resend.webhooks.verify({
payload: rawBody,
headers: {
id: request.headers['webhook-id'],
timestamp: request.headers['webhook-timestamp'],
signature: request.headers['webhook-signature'],
},
webhookSecret: process.env.POSTAL_WEBHOOK_SECRET, // whsec_...
});Prueba y cambia, con las dos a mano
Cambia la URL base y la clave en el entorno de pruebas, envíate un correo electrónico a ti mismo y síguelo en Registros y en los webhooks. Las direcciones de prueba aceptan +rótulo, no consumen cuota, no afectan a la reputación y generan webhooks reales (dependen del plan). Después, haz el cambio en producción y deja Resend a mano unos días.
- ID diferentes. Los ID de correos y de webhooks de Resend no existen en Postal, y al revés tampoco. Guarda de qué proveedor salió cada correo y sigue recibiendo eventos de Resend para lo que salió por allí
- Marca el origen. Usa
tags(por ejemplo,provider: postal) y unaIdempotency-Keypropia en cada envío - Un proveedor a la vez en el mismo flujo. Las invitaciones, los restablecimientos de contraseña y los códigos deben salir por un solo camino. Vigila los rebotes los primeros días, porque el límite de reputación vale para todo el equipo
- Dirección que volvió a funcionar. Un rebote permanente pone la dirección en la lista de supresión; quítala con
DELETE /suppressions/{id}oPOST /suppressions/batch/remove
| En Resend | En Postal |
|---|---|
[email protected] | [email protected] |
[email protected] | [email protected] |
[email protected] | [email protected] |
[email protected] | [email protected] |
| sin equivalente | [email protected], que añade Postal |
Lo que cambia de uno a otro
| Aspecto | Qué cambia en Postal |
|---|---|
| URL base y clave | https://api.postal.so y Authorization: Bearer ps_…. Claves de acceso total o solo de envío, opcionalmente restringidas a un dominio. |
| Envío y respuesta | POST /emails, devuelve {"id"}. Igual. |
| Errores | {"statusCode", "name", "message"}. Igual. Consulta Errores. |
| Webhooks y firma | Estándar Standard Webhooks: webhook-id, webhook-timestamp y webhook-signature, y los mismos valores en svix-*. El secreto es el signing_secret de cada endpoint. |
| Nombres de los eventos | Los mismos, más email.replied, contact.*, form.submitted, broadcast.sent y sending.paused / sending.resumed. |
| Motivo del rebote y del fallo | data.bounce.message y data.failed.reason, con los mismos campos. |
| Adjuntos | filename y content en base64, más path (URL) y content_id para imagen incrustada. Hasta 40 MB por correo electrónico; el plan puede permitir menos. |
| Idempotencia | Encabezado Idempotency-Key (de 1 a 256 caracteres, guardado durante 24 horas) en POST /emails, /emails/batch y /events. |
| Programación | scheduled_at en ISO 8601, en el futuro y como máximo a 30 días. Se puede reprogramar y cancelar. |
| Límites y cuotas | Cada plan tiene sus solicitudes por segundo y cuotas de envío. Consulta Límites y Precios. |
| Pausa por reputación | Los rebotes permanentes por encima del límite del nivel de la reputación de la cuenta (del 3% al 5%), las quejas por encima del 0,3% o el contenido rechazado por encima del 5% pausan los envíos del equipo: 403 sending_paused. Las cuentas nuevas empiezan en el nivel 1, con un tope de 500 correos por día. Antes de la pausa, propietarios y administradores reciben un aviso por correo electrónico. |
| Regiones de envío | São Paulo (sa-east-1, la predeterminada), us-east-1, us-east-2, eu-west-1 y ap-northeast-1, por dominio. |
| Facturación | En reales, dólares o euros. La lista pública de planes está en GET /plans. |
| Retención | Los correos electrónicos, los eventos de webhook y los registros se conservan 30 días; los eventos enviados por POST /events, 90. |
Lo que Postal añade
- Región de procesamiento a tu elección, con São Paulo como predeterminada. Cómo protegemos los datos
- Buzones de entrada para recibir y responder correos electrónicos, con conversaciones, carpetas, etiquetas y borradores. Ver buzones de entrada
- Audiencia y marketing: contactos, segmentos, temas, formularios, campañas y automatizaciones, con la misma clave. Ver el marketing por correo
- Integraciones con Stripe, Supabase, Clerk, Auth0 y Firebase, y la CLI
postal. Integra tu app
La referencia completa está en Migrar de Resend, en la documentación.
Antes de cambiar la clave
- Dominio verificado en Postal y clave de API guardada en el entorno
- Lista de supresión y contactos importados
- Plantillas recreadas y publicadas, con las mismas variables
- Webhook creado y firma comprobada con un evento de prueba
- Un correo electrónico de prueba enviado y visto en Registros
- Tu código guarda de qué proveedor salió cada correo electrónico
- Rebotes revisados en los primeros días después del cambio
Preguntas frecuentes
¿Necesito cambiar el código que usa el SDK de Resend?
En Node.js, basta con pasar baseUrl al crear el cliente o definir RESEND_BASE_URL, además de cambiar la clave. Otros SDK que permiten cambiar la URL base usan el mismo formato, pero no se probaron uno por uno.
¿Puedo mantener el mismo dominio en las dos plataformas?
Sí. Los registros de DNS son diferentes (Postal usa el selector postal), así que los dos quedan verificados al mismo tiempo durante el cambio.
¿Cambia el código que verifica la firma de los webhooks?
resend.webhooks.verify sigue sirviendo, porque Postal acepta los encabezados webhook-* y svix-*. Lo que cambia es el secreto: usa el whsec_… del endpoint creado en Postal.
¿Los IDs de mis correos electrónicos siguen valiendo?
No. Los IDs son de cada plataforma. Guarda también de qué proveedor salió cada correo electrónico, para consultar el estado en el lugar correcto.
Sigue por aquí
Otras guías
Cada plataforma tiene el suyo: el paso a paso general está en Migrar.
¿Listo para cambiar la URL base?
Crea tu cuenta y empieza con el plan Gratis. Para planificar la cuota y el orden de la migración, habla con nosotros por el formulario de contacto.