Novedad: campañas, automatizaciones y correos del producto en la misma cuenta. Conoce Postal
Iniciar sesión Crear cuenta

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
1

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=none y 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
2

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

Node.js
- 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

.env
# Sin tocar el código que ya usa new Resend(clave)
RESEND_BASE_URL=https://api.postal.so
RESEND_API_KEY=ps_sua_chave

Por HTTP

cURL
- 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

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.

3

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
POST /suppressions/batch/add
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]"]}'
4

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 con POST /webhooks/{id}/events/{event_id}/replay
  • Otros lenguajes: consulta la verificación de la firma
Node.js, SDK de Resend
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_...
});
5

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 una Idempotency-Key propia 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} o POST /suppressions/batch/remove

Lo que cambia de uno a otro

Qué sigue igual y qué cambia
AspectoQué cambia en Postal
URL base y clavehttps://api.postal.so y Authorization: Bearer ps_…. Claves de acceso total o solo de envío, opcionalmente restringidas a un dominio.
Envío y respuestaPOST /emails, devuelve {"id"}. Igual.
Errores{"statusCode", "name", "message"}. Igual. Consulta Errores.
Webhooks y firmaEstá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 eventosLos mismos, más email.replied, contact.*, form.submitted, broadcast.sent y sending.paused / sending.resumed.
Motivo del rebote y del fallodata.bounce.message y data.failed.reason, con los mismos campos.
Adjuntosfilename 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.
IdempotenciaEncabezado Idempotency-Key (de 1 a 256 caracteres, guardado durante 24 horas) en POST /emails, /emails/batch y /events.
Programaciónscheduled_at en ISO 8601, en el futuro y como máximo a 30 días. Se puede reprogramar y cancelar.
Límites y cuotasCada plan tiene sus solicitudes por segundo y cuotas de envío. Consulta Límites y Precios.
Pausa por reputaciónLos 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íoSão Paulo (sa-east-1, la predeterminada), us-east-1, us-east-2, eu-west-1 y ap-northeast-1, por dominio.
FacturaciónEn reales, dólares o euros. La lista pública de planes está en GET /plans.
RetenciónLos 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.

¿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.