Guía de migración
Migrar de Postmark a Mail.Land
Postmark y Mail.Land reciben ambos una llamada de envío en JSON, así que la mayoría de los campos se corresponden uno a uno. Esta guía enumera cada campo habitual, los ajustes que debe cambiar en SMTP y las diferencias que requieren cambios en el código.
# Before (Postmark)
SMTP_HOST=smtp.postmarkapp.com # broadcasts: smtp-broadcasts.postmarkapp.com
SMTP_PORT=587 # STARTTLS
SMTP_USER=<Server API token, or SMTP token access key>
SMTP_PASS=<Server API token, or SMTP token secret key>
# After (Mail.Land)
SMTP_HOST=smtp.mail.land
SMTP_PORT=587 # STARTTLS
SMTP_USER=apikey # any value; Mail.Land ignores it
SMTP_PASS=ml_pet_... # transactional; use an ml_pem_ key for broadcastsConceptos de Postmark en Mail.Land
| Postmark | Mail.Land |
|---|---|
| Server y Server API token | Espacio de trabajo y claves de API (credenciales), cada una limitada a la API o a SMTP, a dominios y a direcciones IP |
| Flujos de mensajes (transaccional, masivo) | El flujo de la clave: transaccional (ml_pet_) o marketing (ml_pem_) |
| Firmas de remitente y dominios | Dominio verificado (SPF, DKIM, DMARC y return path) |
| Plantillas y diseños (Mustachio) | Plantillas con marcadores {{name}} |
| Supresiones por flujo de mensajes | Supresiones con un tipo y un ámbito |
| Webhooks | Webhooks firmados con HMAC-SHA256, con un secreto por webhook |
| Flujo de entrada | Rutas de entrada |
Cree las claves de API adecuadas
En Postmark, el campo o la cabecera MessageStream elige el flujo. En Mail.Land lo hace la clave: su prefijo indica su modo y su flujo. Cree una clave por aplicación y por flujo.
| Prefijo | Para qué sirve |
|---|---|
ml_pet_ | Producción, transaccional: recibos, restablecimiento de contraseñas, alertas. Hasta 5 destinatarios por solicitud. |
ml_pem_ | Producción, marketing: boletines y envíos masivos con send-batch, hasta 10.000 destinatarios por llamada. |
ml_set_ | Pruebas, transaccional: con sandboxBehavior en LogOnly (el valor por defecto), las solicitudes se aceptan y se registran, pero nunca se entregan. Úsela para sus pruebas de integración. |
Cada clave puede limitarse a la API, a SMTP o a ambos, a dominios seleccionados y a direcciones IP conocidas. La clave se muestra una sola vez y Mail.Land solo guarda un hash de ella.
Cambie SMTP: modifique el host y las credenciales
Postmark usa un Server API token o un SMTP token, y una cabecera para elegir el flujo. Mail.Land usa su clave de API como contraseña, ignora el nombre de usuario y toma el flujo de la clave.
| Ajuste | Postmark | Mail.Land |
|---|---|---|
| Host | smtp.postmarkapp.com, o smtp-broadcasts.postmarkapp.com para envíos masivos | smtp.mail.land |
| Puerto | 25, 587 o 2525, con STARTTLS | 587 con STARTTLS |
| Nombre de usuario | Server API token, o access key del SMTP token | Cualquier valor, por ejemplo apikey |
| Contraseña | Server API token, o secret key del SMTP token | Una clave de API de Mail.Land con SMTP habilitado |
| Flujo | Cabecera X-PM-Message-Stream | La clave: ml_pet_ para transaccional, ml_pem_ para marketing |
Mail.Land solo acepta el inicio de sesión después de STARTTLS. Mail.Land no tiene en cuenta la cabecera X-PM-Message-Stream: la clave determina el flujo. Los mensajes pueden ocupar hasta 15 MiB.
Cambie la llamada a la API
Ambas API reciben JSON. Postmark usa nombres en PascalCase y la cabecera X-Postmark-Server-Token; Mail.Land usa nombres en camelCase y la cabecera X-API-Key. Este es el mismo mensaje en ambas, y un envío con plantilla.
curl -X POST https://api.postmarkapp.com/email \
-H "X-Postmark-Server-Token: $POSTMARK_SERVER_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"From": "Your Shop <[email protected]>",
"To": "[email protected]",
"ReplyTo": "[email protected]",
"Subject": "Order 12345 has shipped",
"TextBody": "Your order is on its way.",
"HtmlBody": "<p>Your order is on its way.</p>",
"Tag": "order-shipped",
"MessageStream": "outbound"
}'curl -X POST https://api.mail.land/v1/transactional/send \
-H "X-API-Key: $MAILLAND_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": { "email": "[email protected]", "name": "Your Shop" },
"to": [{
"email": "[email protected]", "name": "Jane",
"variables": { "orderNumber": "12345" }
}],
"replyTo": "[email protected]",
"subject": "Order {{orderNumber}} has shipped",
"textContent": "Your order {{orderNumber}} is on its way.",
"htmlContent": "<p>Your order {{orderNumber}} is on its way.</p>",
"tags": ["order-shipped"]
}'
# MAILLAND_API_KEY=ml_pet_... (production, transactional)Correspondencia de campos de la llamada de envío
De POST /email y /email/withTemplate a POST /v1/transactional/send.
| Postmark | Mail.Land | Nota |
|---|---|---|
X-Postmark-Server-Token: <token> | X-API-Key: <key> | Otra cabecera. |
From | from.email, from.name | Separe el nombre visible de la dirección. La dirección debe pertenecer a un dominio verificado. |
To | to[].email, to[].name | Un array de objetos en lugar de una cadena separada por comas. Hasta 5 destinatarios por solicitud con una clave transaccional; cada uno recibe una copia separada. |
Cc, Bcc | Ninguno | No se admite. Envíe un mensaje separado a cada destinatario. |
Subject | subject | Obligatorio salvo que use una plantilla. |
HtmlBody | htmlContent | |
TextBody | textContent | |
ReplyTo | replyTo | Una sola cadena de dirección. Si usaba varias direcciones reply-to separadas por comas, conserve una. |
Tag | tags | Un array, de modo que un mensaje puede llevar varias etiquetas. |
Metadata | Ninguno | No hay campo de metadatos de clave y valor. Use tags o una cabecera personalizada. |
Headers [{ Name, Value }] | customHeaders | Un objeto con nombres y valores de cabecera en lugar de un array. |
TrackOpens | tracking.opens | La opción de seguimiento de aperturas del propio dominio tiene prioridad. |
TrackLinks | tracking.clicks | Un booleano en lugar de None, HtmlAndText, HtmlOnly o TextOnly. |
Attachments[].Name, Content, ContentType, ContentID | attachments[].fileName, content, contentType, contentId, inline | Contenido Base64 como hasta ahora. Indique inline: true para una imagen incrustada. Hasta 10 adjuntos y 10 MiB en total. |
MessageStream | Ninguno | La clave determina el flujo: ml_pet_ o ml_pem_. |
TemplateId, TemplateAlias | templateId | Vuelva a crear antes la plantilla en Mail.Land; recibirá un ID nuevo. |
TemplateModel | variables, to[].variables | Solo valores planos, porque los marcadores no admiten secciones ni bucles. |
POST /email/batch (hasta 500 mensajes) | POST /v1/transactional/send-batch | Un modelo distinto: contenido compartido para hasta 10.000 destinatarios, cada uno con sus variables, enviado con una clave de marketing. Para mensajes distintos, haga una llamada de envío por cada uno. |
Sin equivalente | scheduledAt | Programe un mensaje con hasta 7 días de antelación. |
Sin equivalente | Cabecera X-Idempotency-Key | Dentro del periodo de conservación, un reintento con la misma clave devuelve la respuesta original en lugar de volver a enviar. |
Mail.Land responde 202 Accepted con un jobId, un ID de mensaje por cada destinatario aceptado y las listas de destinatarios aceptados y rechazados.
Añada su dominio y los registros DNS
Añada el dominio en Mail.Land y publique los registros que muestra el panel. SPF, DKIM y DMARC deben verificarse antes de poder enviar. Puede hacerlo mientras Postmark sigue enviando.
| Registro | Nombre | Valor | Nota |
|---|---|---|---|
| SPF (TXT) | yourdomain.com | include:spf.mlnd.net | Añada el include a su registro SPF actual. Un dominio solo debe tener un registro SPF. |
| DKIM (TXT) | mlnd._domainkey.yourdomain.com | v=DKIM1; k=rsa; p=... | El selector es mlnd, así que el registro convive con el registro DKIM de Postmark. |
| DMARC (TXT) | _dmarc.yourdomain.com | v=DMARC1; p=... | Obligatorio. Mantenga un único registro DMARC; basta con uno existente que tenga una política. |
| Return path (CNAME) | bounce.yourdomain.com | bounce.mlnd.net | Configúrelo como DNS only, sin proxy. Compruebe que ese nombre no esté ya en uso. |
Mantenga los registros DNS de Postmark hasta terminar el traslado y elimínelos solo cuando haya salido su último mensaje.
Importe primero sus listas de supresión
Postmark guarda las supresiones por flujo de mensajes. Exporte la lista de cada flujo, por ejemplo con GET /message-streams/<stream>/suppressions/dump, e impórtela en Mail.Land con el tipo y el ámbito correspondientes, en el panel o con POST /v1/transactional/suppressions/import.
| Motivo en Postmark | Tipo en Mail.Land | Ámbito |
|---|---|---|
| HardBounce | HardBounce | Global |
| SpamComplaint | Complaint | Global |
| ManualSuppression en un flujo masivo (bajas) | Unsubscribe | Marketing; o List, que solo se aplica a las campañas enviadas a la lista de correo correspondiente de Mail.Land |
| ManualSuppression en un flujo transaccional | Manual | Transactional |
Importe antes de su primer envío a través de Mail.Land, para que quienes se dieron de baja sigan suprimidos desde el principio. Las direcciones que ya existen se omiten, no se duplican.
Dirija sus webhooks a Mail.Land
Añada su endpoint en Mail.Land, elija los eventos y verifique la cabecera X-MLND-Signature con el secreto de ese webhook. Si protegía su endpoint de Postmark con autenticación Basic o una lista de IP permitidas, verifique la firma en su lugar.
| Webhook de Postmark | Evento de Mail.Land | Nota |
|---|---|---|
Entrega (Delivery) | message.delivered | |
Rebote (Bounce) | message.bounced | Cubre rebotes duros y blandos. Los retrasos temporales son message.deferred. |
Queja por spam (Spam complaint) | message.complained | |
Cambio de suscripción (Subscription change) | message.unsubscribed | Se envía cuando el destinatario se da de baja por sí mismo, no por cambios que usted haga mediante la API. |
Apertura, clic (Open, Click) | Aún no disponible | Los eventos de apertura y clic están catalogados, pero hoy no se envían. |
Entrada (Inbound) | inbound.received | Configure una ruta de entrada para la dirección receptora. |
Error de SMTP API | Ninguno | El relé SMTP rechaza el mensaje durante la propia sesión, con un código de respuesta. |
Elimine duplicados por event_id, ya que una entrega reintentada lleva el mismo ID de evento. Un estado puede corregirse más tarde, así que siga procesando eventos después de uno final y compare status_version.
Vuelva a crear sus plantillas
Cree cada plantilla en el panel de Mail.Land o con la API de plantillas y envíela con templateId y variables. Cada marcador necesita un valor; si falta, el envío se rechaza con 422 y los nombres que faltan.
| Postmark | Mail.Land |
|---|---|
{{firstName}} | {{firstName}} |
{{#each items}} ... {{/each}} | Ninguno |
{{#section}}, {{^section}} | Ninguno |
Plantillas de diseño (layout) | Ninguno |
Los marcadores de Mail.Land son simples: sin secciones, bucles ni diseños. Pase valores planos, construya las listas en su aplicación y repita el HTML común de cabecera y pie en cada plantilla.
Cambie paso a paso
- Añada y verifique su dominio de envío en Mail.Land, agregando include:spf.mlnd.net a su registro SPF actual.
- Exporte las supresiones de cada flujo de mensajes de Postmark e impórtelas.
- Cree una clave transaccional para cada aplicación y una clave de marketing si hace envíos masivos.
- Vuelva a crear sus plantillas y webhooks.
- Cree una clave de pruebas con sandboxBehavior en LogOnly y ejecute sus pruebas de integración contra Mail.Land sin entregar correo.
- Envíe mensajes reales a direcciones que usted controle y revíselos en el registro de correo.
- Traslade una aplicación o una pequeña parte del tráfico y vigile la entrega, los rebotes y las quejas.
- Mientras funcionen ambos proveedores, mantenga sincronizadas las bajas en los dos sentidos: copie las nuevas bajas, quejas y rebotes de Postmark a Mail.Land y de Mail.Land a Postmark, y haga una sincronización final antes de trasladar el tráfico restante.
- Revise el calentamiento de su dominio: en Free y Pro, un dominio nuevo tiene un límite diario que sube por etapas, y los envíos que lo superan se rechazan. Planifique el traslado dentro de esos límites o consúltenos un calendario a medida. Max no pasa por calentamiento.
- Después traslade el resto y mantenga Postmark activo hasta que hayan salido sus últimos mensajes.
Fuentes
Páginas públicas de Postmark utilizadas en esta guía.
- [1]Postmark Email APIhttps://postmarkapp.com/developer/api/email-apicomprobado el 4 de noviembre de 2026
- [2]Postmark Templates APIhttps://postmarkapp.com/developer/api/templates-apicomprobado el 4 de noviembre de 2026
- [3]Postmark: envío de correo por SMTPhttps://postmarkapp.com/developer/user-guide/send-email-with-smtpcomprobado el 4 de noviembre de 2026
- [4]Postmark: introducción a los webhookshttps://postmarkapp.com/developer/webhooks/webhooks-overviewcomprobado el 4 de noviembre de 2026
- [5]Postmark Suppressions APIhttps://postmarkapp.com/developer/api/suppressions-apicomprobado el 4 de noviembre de 2026
Preguntas frecuentes
¿Puedo seguir enviando a través de Postmark mientras hago pruebas?
Sí. Verificar un dominio en Mail.Land no impide que Postmark siga enviando en su nombre, así que puede usar ambos y trasladar el tráfico gradualmente.
¿Mail.Land admite cc y bcc?
No en la llamada de envío. Cada destinatario recibe una copia separada, así que envíe un mensaje aparte donde usaba cc o bcc.
¿Cómo sustituyo mi flujo de envíos masivos?
Use una clave de marketing (ml_pem_) con send-batch para los envíos por API, o cree una campaña en el panel con el plan Pro o Max.
¿Existe un SDK oficial de Mail.Land?
Todavía no. La documentación incluye ejemplos listos para copiar de la API REST en cURL, Node.js, Python, PHP, C#, Java y Go, y cualquier biblioteca SMTP funciona con el relé.
¿Listo para dejar Postmark?
Cree una cuenta gratuita y envíe hoy mismo su primera prueba. Si desea ayuda, cuéntenos cómo es su configuración.