Migrar desde Mandrill
Esta página mapea el payload messages/send de Mandrill (Mailchimp Transactional), la rejection blacklist y los webhooks a Bird. Sigue la guía principal de migración en orden y usa estas correspondencias para los pasos 1, 3 y 4.
Dos cambios de estructura dominan la migración. Mandrill anida todo bajo un objeto message y se autentica con un key en el cuerpo de la solicitud. Nosotros usamos un payload plano de primer nivel y un header Authorization: Bearer estándar. Además, el type de destinatario de Mandrill (to/cc/bcc como campo en cada dirección) pasa a ser nuestros arrays separados to/cc/bcc.
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 Bird que ya tenga disponible: el servidor MCP si hay uno conectado, o CLI si está instalado y con sesión iniciada.
Ejemplo de código
I am moving an email integration from Mandrill 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/mandrill.md for the payload, suppression and event mapping, and https://bird.com/docs/guides/email/migrate.md for the order the steps go in.
3. Find and list my Mandrill usage in this repository before you change anything: the messages/send and messages/send-template call sites and any SDK wrappers around them, the webhook handler and the URL it is registered at, and every domain I send from. Mandrill authenticates with an API key passed in the request body rather than a header, so tell me every place that key appears in my code: the port changes how I authenticate, not just what I send, and that key is a secret currently sitting in a payload.
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 my current provider 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 rejects from Mandrill 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. Read the list through rejects/list, and skip the soft-bounce rows: those are transient failures rather than suppressions, and importing them would suppress addresses that are fine. Show me how many rows you skipped. 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. Move the API key out of the request body and into the Authorization header as a bearer token while you are there. Bird signs deliveries per Standard Webhooks rather than Mandrill's scheme, so treat verification as a rewrite rather than a URL change: 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 Mandrill path is a separate step that comes later: ask me again and wait for me to reply with the words retire the Mandrill 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
| Función | Mandrill (messages/send) | Bird |
|---|---|---|
| Autenticación | key en el cuerpo de la solicitud | Header Authorization: Bearer bk_... |
| Remitente | message.from_email / from_name | from: string o { "email", "name" } |
| Destinatarios | message.to: [{ email, name, type }] | to / cc / bcc: separados por el campo type |
| Asunto | message.subject | subject |
| Cuerpo | message.html / message.text | html / text (al menos uno) |
| Reply-to | message.headers["Reply-To"] | reply_to: array |
| Headers personalizados | message.headers | headers: objeto string → string |
| Etiquetas filtrables | message.tags: strings simples | tags: pares { name, value } |
| Contexto de ida y vuelta | message.metadata | metadata: JSON arbitrario |
| Plantilla almacenada | messages/send-template + merge_vars | template + template.parameters |
| Programación | send_at | scheduled_at |
| Seguimiento de aperturas/clics | message.track_opens / track_clicks | track_opens / track_clicks (default true) |
| Adjuntos | message.attachments: { type, name, content } | attachments: { content_type, filename, content } |
| Imágenes en línea | message.images: { type, name, content } | attachments con content_id |
| Categoría | subaccount / convención de tags | category: marketing (default) o transactional |
Los límites y valores por defecto de nuestros campos (cantidad de destinatarios, límites de tags y metadata) están en Enviar email.
Notas de migración:
- Aplana y reautentica. Elimina el wrapper message (sus campos pasan al nivel superior) y mueve la clave API del cuerpo al header Authorization. El campo key no tiene equivalente aquí.
- Separa los destinatarios por type. Mandrill marca cada destinatario como to, cc o bcc en el objeto de dirección. Nosotros usamos tres arrays separados. Distribuye la lista to por su campo type durante la migración.
- Los tags pasan a ser pares nombre/valor. Los tags de Mandrill son strings simples ("welcome"). Nuestros tags son pares { name, value }. Elige un name estable, por ejemplo { "name": "category", "value": "welcome" }, para que tus filtros y analíticas agrupen como lo hacían tus estadísticas de Mandrill. metadata se traslada directamente como JSON.
- Las plantillas almacenadas se trasladan, con una salvedad. El messages/send-template de Mandrill pasa a ser nuestro campo template (referenciado por ID o slug) con valores en template.parameters. Consulta enviar con una plantilla. Nuestras variables de plantilla son por mensaje en lugar de por destinatario, así que las merge_vars por destinatario se convierten en una entrada de batch por destinatario, cada una con su propio parameters.
- Los adjuntos se trasladan directamente. El content en base64 de Mandrill es nuestro content, y las images en línea (referenciadas como cid: en el HTML) pasan a ser entradas attachments con content_id. Consulta adjuntos.
Exportar supresiones
Mandrill guarda las direcciones no deseadas en su rejection blacklist. Extráela con rejects/list API (o exporta desde la vista Rejection Blacklist). Cada entrada tiene un motivo (hard-bounce, soft-bounce, spam, unsub, custom). Omite las filas soft-bounce porque describen fallos transitorios, no supresiones reales. Pasa el resto por el ciclo de importación.
Traducir eventos de webhook
Mandrill envía arrays de eventos agrupados; mapea el valor event al vocabulario de eventos de Bird:
| Resultado | Mandrill | Bird |
|---|---|---|
| Enviado / aceptado | send | email.accepted → email.processed |
| Entregado | delivered | email.delivered |
| Fallo temporal | deferral | email.deferred |
| Rebote permanente | hard_bounce | email.bounced / email.out_of_band_bounce |
| Rebote suave | soft_bounce | email.deferred (luego email.bounced si desiste) |
| Queja de spam | spam | email.complained |
| Cancelación de suscripción | unsub | email.unsubscribed / email.list_unsubscribed |
| Rechazado/bloqueado | reject | email.rejected |
| Apertura | open | email.opened |
| Clic | click | email.clicked |
Dos diferencias que debes contemplar en el código:
- La aceptación se divide en dos eventos aquí. El send de Mandrill significa que el mensaje fue inyectado y delivered significa que el servidor receptor lo aceptó, que es la misma distinción que hacemos nosotros. La diferencia está solo de nuestro lado: separamos la aceptación (email.accepted) del procesamiento (email.processed) antes de email.delivered, así que un handler que dependía solo de send ahora tiene dos eventos entre los que elegir.
- Los eventos tienen alcance por destinatario y se firman de forma diferente. Nuestros eventos de entrega incluyen recipient_id junto con email_id, con un flujo por destinatario. Enviamos un evento por solicitud y lo firmamos según la especificación Standard Webhooks. Mandrill, en cambio, firma arrays agrupados con un X-Mandrill-Signature HMAC. Consulta Webhooks y eventos para la verificación.
Puesta en producción
Sigue dominios y DNS y la prueba de humo en sandbox en 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 vas a redirigir
- Webhooks y eventos: configuración del endpoint y verificación con Standard Webhooks
- Sandbox de pruebas: prueba de humo de la nueva integración antes de la puesta en producción
- Supresiones: confirma tu lista importada y cómo la mantenemos de aquí en adelante
Recursos relacionados
Continúa con la documentación, guías y ejemplos sobre este tema. Los recursos están en inglés.
Ver la guíaGetting started with emailExplorar la funcionalidadEmailSeguir la ruta de aprendizajeBuild your first integrationGuía de implementaciónSend your first email
Prueba el ejercicio y obtén un resumen de implementación