# 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](/docs/guides/email/migrate) 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](#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.

```text
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`](/docs/api/reference/create-email-message) 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](/docs/guides/email/sending-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 para `from`, `to`, `cc`, `bcc` como para `reply_to`.
- **Los `tags` son strings simples con un máximo de cinco. Nuestros tags son pares.** Un tag como `"welcome"` se convierte en `{"name": "category", "value": "welcome"}`. Elige un `name` estable para que tus dashboards filtren como lo hacían tus estadísticas de tags en MailerSend.
- **`personalization` son datos de plantilla por destinatario.** Es un array indexado por email del destinatario. Nuestro `template.parameters` se 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](/docs/guides/email/sending-bulk) cada uno. Si tu array `personalization` lleva los mismos valores para todos los destinatarios, se reduce a un solo objeto `template.parameters`.
- **El contexto de ida y vuelta no tiene equivalente en MailerSend que copiar.** Si reconstruías el contexto a partir de `tags`, usa [`metadata`](/docs/guides/email/sending-email) en su lugar: lo devolvemos en cada evento de webhook junto con `email_id`/`recipient_id`, y no tiene un límite de cinco entradas.
- **`precedence_bulk` no tiene equivalente, y `category` no lo es.** `precedence_bulk` establece una cabecera `Precedence: bulk`, que pide a los autoresponders y agentes de fuera de oficina que no respondan. Nuestro `category` decide qué registros de supresión y preferencias de cancelación de suscripción pueden bloquear un mensaje, y nada más. Usar `category` en su lugar cambia el comportamiento de supresión y no hace nada respecto a los autoresponders. Si necesitas la cabecera, ten en cuenta que `headers` rechaza direcciones y nombres de plataforma, pero no esta.
- **Las cabeceras personalizadas y `list_unsubscribe` están restringidas por plan en MailerSend.** Si tu plan no las incluía, aquí están disponibles; consulta [envío de email](/docs/guides/email/sending-email) y [categorías](/docs/guides/email/categories) 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](/docs/guides/email/migrate#3-import-suppressions):

- **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](/docs/guides/email/events), 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](/docs/guides/email/suppressions) 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](https://www.standardwebhooks.com), 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](/docs/guides/webhooks).

**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](/docs/guides/email/migrate#2-re-point-domains-and-dns) y la [prueba de humo en sandbox](/docs/guides/email/migrate#5-verify-in-the-sandbox-before-cutover) de la guía principal. Ambos son independientes del proveedor.

## Próximos pasos

- [Dominios de envío](/docs/guides/email/sending-domains): registro, ciclo de vida de verificación y los registros DNS que estás publicando
- [Webhooks y eventos](/docs/guides/webhooks): configuración de endpoints y verificación de Standard Webhooks
- [Sandbox de pruebas](/docs/guides/email/testing-sandbox): prueba de humo de la nueva integración antes de la transición
- [Supresiones](/docs/guides/email/suppressions): confirma tu lista importada y cómo la mantenemos a partir de ahora

## Related resources

- [Getting started with email](/learn/email/getting-started-with-email) (video)
- [Email](/products/email) (product)
- [Build your first integration](/learn/paths/integration) (course)
- [Send your first email](/docs/get-started/send-your-first-email) (docs)

[Get an implementation brief](/learn/workspace?topic=email)
