Migrar desde MailerSend
Esta página mapea el payload de envío, las listas de supresión y los webhooks de MailerSend a Bird. Sigue la guía principal de migración en orden y usa estos mapeos para los pasos 1, 3 y 4.
MailerSend mantiene cinco listas de supresión y una de ellas es temporal por diseño. Lee Exportar supresiones antes del paso 3: importar esa lista convierte una retención de 72 horas en un bloqueo permanente.
Pasa esto a tu agente
Pega esto en Claude Code, Cursor o Codex. El agente recorre esta página contra tu propio repositorio, usando la superficie de Bird que ya tenga: el servidor MCP si hay uno conectado, o CLI si está instalado y con sesión iniciada.
I am moving an email integration from MailerSend to Bird. Route through it with me.
1. Check what you already have before setting anything up. If Bird's MCP server is connected, use its tools. If the Bird CLI is installed and signed in, use that. Either one is enough, and every step below is an action you take with whichever you have. Only if neither is present, follow https://bird.com/docs/ai/set-up-your-agent.md to set one up and sign me in. Every Bird docs page serves Markdown at its own URL with `.md` appended, so fetch that rather than the HTML.
2. Read https://bird.com/docs/guides/email/migrate/mailersend.md for the payload, suppression and webhook mapping, and https://bird.com/docs/guides/email/migrate.md for the order the steps go in.
3. Find and list my MailerSend usage in this repository before you change anything: calls to /v1/email and any SDK wrappers around them, whether I send with template_id and personalization or with html, the webhook handler and the URL it is registered at, and every domain I send from. Tell me the list before you edit anything.
4. Register each of those sending domains with Bird and give me the DNS records to publish, following https://bird.com/docs/guides/email/sending-domains.md. Leave every DNS record MailerSend uses exactly as it is: Bird's records are published alongside them and both providers authenticate side by side until I switch traffic. Publishing DNS affects mail for the whole domain, so show me the records and let me publish them.
5. Export my suppressions and import them into Bird before any production traffic goes through Bird, so my first sends do not reach addresses that already bounced or complained. MailerSend keeps five lists under /v1/suppressions: hard bounces, spam complaints, unsubscribes and the blocklist are the four to carry over. Do NOT import the On Hold list: it is a temporary 72-hour hold that MailerSend clears by itself, and importing it would suppress those addresses permanently. The Bird import takes one address per request and is idempotent, so a partial re-run is safe. https://bird.com/docs/guides/email/suppressions.md has the reason taxonomy.
6. Port the send call and the webhook handler using the mapping tables on the provider page. Both providers sign with HMAC-SHA256, so this is a re-implementation rather than new code: MailerSend sends one Signature header, Bird follows the Standard Webhooks scheme with webhook-id, webhook-timestamp and webhook-signature, and the signed string is constructed differently. Keep the constant-time comparison. https://bird.com/docs/guides/webhooks.md and https://bird.com/docs/guides/email/events.md.
7. Run my whole integration against Bird's mail sandbox before any production traffic, following https://bird.com/docs/guides/email/testing-sandbox.md. Sandbox sends run the real pipeline without reaching an inbox or touching my sending reputation.
8. Stop and ask me wherever a step needs a decision. Do not point production traffic at Bird until I have seen the sandbox results and replied with the words cut over to Bird. Retiring the MailerSend path is a separate step that comes later: ask me again and wait for me to reply with the words retire the MailerSend path. A reply that agrees without naming what it is authorising is not authorisation. Finish by telling me what is left that only a person can do.Mapear la llamada de envío
POST /v1/email de MailerSend y nuestro POST /v1/email/messages comparten estructura. Las diferencias que requieren código son el límite de tags y cómo se transportan los datos por destinatario.
| Función | MailerSend | Bird |
|---|---|---|
| Remitente | from ({email, name}) | from |
| Destinatarios | to (máx. 50), cc / bcc (máx. 10 cada uno) | to / cc / bcc (arrays) |
| Asunto | subject (máx. 998 caracteres) | subject |
| Cuerpo | html / text | html / text (al menos uno) |
| Reply-to | reply_to ({email, name}) | reply_to (array) |
| Cabeceras personalizadas | headers ({name, value}, planes superiores) | headers (objeto string → string) |
| Etiquetas filtrables | tags (array de strings, máx. 5) | pares tags: {name, value} |
| Plantilla almacenada | template_id + personalization | template + template.parameters (ver más abajo) |
| Adjuntos | attachments (content, filename, disposition, id) | attachments |
| Programación | send_at (hasta 72 horas de anticipación) | scheduled_at |
| Precedencia masiva | precedence_bulk | (sin equivalente, ver más abajo) |
| Categoría | (ninguna) | category: marketing (predeterminado) o transactional |
| Threading | in_reply_to | headers |
Los límites y valores predeterminados de nuestros campos (cantidad de destinatarios, límites de tags y metadatos) están en Envío de email.
Notas de migración:
- Las direcciones son objetos allí y strings aquí.
{"email": "a@x.com", "name": "A"}se convierte en"A <a@x.com>"o simplemente"a@x.com", tanto parafrom,to,cc,bcccomo parareply_to. - Los
tagsson strings simples con un máximo de cinco. Nuestros tags son pares. Un tag como"welcome"se convierte en{"name": "category", "value": "welcome"}. Elige unnameestable para que tus dashboards filtren como lo hacían tus estadísticas de tags en MailerSend. personalizationson datos de plantilla por destinatario. Es un array indexado por email del destinatario. Nuestrotemplate.parametersse aplica al envío completo, así que un mensaje cuyo contenido realmente difiere por destinatario se convierte en un envío por destinatario o en una entrada de batch cada uno. Si tu arraypersonalizationlleva los mismos valores para todos los destinatarios, se reduce a un solo objetotemplate.parameters.- El contexto de ida y vuelta no tiene equivalente en MailerSend que copiar. Si reconstruías el contexto a partir de
tags, usametadataen su lugar: lo devolvemos en cada evento de webhook junto conemail_id/recipient_id, y no tiene un límite de cinco entradas. precedence_bulkno tiene equivalente, ycategoryno lo es.precedence_bulkestablece una cabeceraPrecedence: bulk, que pide a los autoresponders y agentes de fuera de oficina que no respondan. Nuestrocategorydecide qué registros de supresión y preferencias de cancelación de suscripción pueden bloquear un mensaje, y nada más. Usarcategoryen su lugar cambia el comportamiento de supresión y no hace nada respecto a los autoresponders. Si necesitas la cabecera, ten en cuenta queheadersrechaza direcciones y nombres de plataforma, pero no esta.- Las cabeceras personalizadas y
list_unsubscribeestán restringidas por plan en MailerSend. Si tu plan no las incluía, aquí están disponibles; consulta envío de email y categorías para ver cómo gestionamos list-unsubscribe en correo de marketing.
Exportar supresiones
MailerSend mantiene cinco listas bajo /v1/suppressions, un endpoint por lista. Cuatro son permanentes y se transfieren; la quinta no debe transferirse.
Exporta y procesa con el bucle de importación:
- Rebotes duros, por ejemplo
GET https://api.mailersend.com/v1/suppressions/hard-bounces - Quejas de spam
- Cancelaciones de suscripción
- Lista de bloqueo, las direcciones y patrones que añadiste manualmente
No importes la lista On Hold. La propia descripción de MailerSend indica que contiene direcciones que "have soft bounced 5 times within 30 days", que "these emails will be blocked for 72 hours", y que luego son "automatically removed from the list". Es un período de enfriamiento que el proveedor limpia por sí mismo, así que importarla convierte una retención temporal en una supresión permanente y deja de enviar correo silenciosamente a direcciones que estaban a punto de ser liberadas. Nuestro equivalente de ese comportamiento es el aplazamiento, que gestionamos a partir de resultados de entrega en tiempo real en lugar de una lista importada.
Los tipos de lista de MailerSend se corresponden con nuestras razones hard_bounce, complaint y manual; Supresiones tiene la taxonomía completa.
Traducir eventos de webhook
| Resultado | MailerSend | Bird |
|---|---|---|
| Aceptado/procesado | activity.sent | email.accepted → email.processed |
| Entregado | activity.delivered | email.delivered |
| Fallo temporal | activity.soft_bounced / activity.deferred | email.deferred |
| Rebote permanente | activity.hard_bounced | email.bounced / email.out_of_band_bounce |
| Queja de spam | activity.spam_complaint | email.complained |
| Apertura | activity.opened / activity.opened_unique | email.opened |
| Clic | activity.clicked / activity.clicked_unique | email.clicked |
| Cancelación de suscripción | activity.unsubscribed | email.unsubscribed / email.list_unsubscribed |
| Retenido temporalmente | recipient.on_hold_added / ..._removed | (sin equivalente; ver más arriba) |
Dos diferencias determinan cuánto cambia tu handler.
Ambos lados firman con HMAC-SHA256, así que es una reimplementación, no código nuevo. MailerSend envía una sola cabecera Signature con un hash del payload calculado con el secreto de firma de ese webhook. Nosotros seguimos el esquema Standard Webhooks, que usa webhook-id, webhook-timestamp y webhook-signature y firma una cadena construida a partir del id, el timestamp y el body, por lo que el timestamp también te da protección contra repetición. Conserva la comparación en tiempo constante que ya tienes y reemplaza la construcción; la receta está en Webhooks y eventos.
MailerSend distingue aperturas y clics de sus variantes únicas. Nosotros no. activity.opened y activity.opened_unique llegan ambos como email.opened, así que un handler que contaba solo la variante única necesita deduplicar por recipient_id directamente. Nuestros eventos de entrega tienen alcance por destinatario, de modo que un envío a tres destinatarios produce tres resultados de entrega en lugar de uno.
Transición
Sigue dominios y DNS y la prueba de humo en sandbox de la guía principal. Ambos son independientes del proveedor.
Próximos pasos
- Dominios de envío: registro, ciclo de vida de verificación y los registros DNS que estás publicando
- Webhooks y eventos: configuración de endpoints y verificación de Standard Webhooks
- Sandbox de pruebas: prueba de humo de la nueva integración antes de la transición
- Supresiones: confirma tu lista importada y cómo la mantenemos a partir de ahora
Recursos relacionados
Continúa con la documentación, guías y ejemplos de este tema.