Migrar desde Mailjet
Esta página mapea el payload Send API v3.1 de Mailjet, la blocklist y el Event API a Bird. Sigue la guía principal de migración en orden y usa estas correspondencias para los pasos 1, 3 y 4.
El mayor cambio estructural es la envoltura. Mailjet envuelve cada envío en un array Messages de objetos PascalCase (POST /v3.1/send). Nosotros recibimos un solo objeto JSON plano y en minúsculas por POST /v1/email/messages, y varios mensajes independientes van al endpoint de lote en lugar del array Messages.
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: el servidor MCP si hay uno conectado, o el CLI si está instalado y con sesión iniciada.
Ejemplo de código
I am moving an email integration from Mailjet 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/mailjet.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 Mailjet usage in this repository before you change anything: the POST /v3.1/send call sites and any SDK wrappers around them, the event handler and the URL it is registered at, and every domain I send from.
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 blocklist from Mailjet and import it into Bird before any production traffic goes through Bird, so my first sends do not reach addresses that already bounced or complained. Read it through the contact-management API, or from the contact statistics pages if that is what I have access to. 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 event handler using the mapping tables on the provider page. Mailjet posts a Messages array of PascalCase objects, and each entry becomes either one flat Bird send or one entry in a batch, so tell me which shape my call sites map onto before you rewrite them. EventPayload becomes metadata. Bird signs deliveries per Standard Webhooks rather than Mailjet'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 Mailjet path is a separate step that comes later: ask me again and wait for me to reply with the words retire the Mailjet 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 | Mailjet (Send API v3.1) | Bird |
|---|---|---|
| Remitente | From: { "Email", "Name" } | from: string o { "email", "name" } |
| Destinatarios | To / Cc / Bcc: [{ "Email", "Name" }] | to / cc / bcc: arrays |
| Asunto | Subject | subject |
| Cuerpo | TextPart / HTMLPart | text / html (al menos uno) |
| Reply-to | ReplyTo: { "Email", "Name" } | reply_to: array |
| Cabeceras personalizadas | Headers | headers: objeto string → string |
| Contexto de ida y vuelta | EventPayload (string), devuelto en eventos | metadata: JSON arbitrario |
| Tu propio ID de envío | CustomID, devuelto en eventos | metadata o tags |
| Plantilla almacenada | TemplateID + Variables | template + template.parameters |
| Adjuntos | Attachments: { "ContentType", "Filename", "Base64Content" } | attachments: { "content_type", "filename", "content" } |
| Imágenes en línea | InlinedAttachments, con ContentID | attachments con content_id |
| Seguimiento | configuración de cuenta/plantilla | track_opens / track_clicks (por defecto true) |
| Categoría | (ninguno) | category: marketing (por defecto) 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 correo electrónico.
Notas de portabilidad:
- Desenvuelve el array Messages. Un envío individual de Mailjet es una entrada en Messages. Aquí, eso es todo el cuerpo de la solicitud. Un array Messages con varias entradas se mapea a nuestro endpoint de lote. Campos repetidos en una sola solicitud no pueden representar el lote.
- La nomenclatura cambia de PascalCase a minúsculas. Todos los campos se renombran: HTMLPart → html, TextPart → text, From.Email → from.email. Es mecánico, pero afecta a cada envío.
- EventPayload pasa a ser metadata. Mailjet devuelve un solo string EventPayload en cada evento. Nosotros devolvemos metadata estructurados (JSON) y tags en cada evento de webhook, así que puedes dividir los datos de correlación en campos tipados. Consulta tags vs metadata.
- CustomID es un identificador de correlación; los reintentos necesitan una solución aparte. El CustomID de Mailjet llega hasta los eventos para seguimiento, pero no deduplica. Su deduplicación es X-Mailjet-DeduplicateCampaign, un booleano que se usa con X-Mailjet-Campaign para evitar que una campaña llegue al mismo destinatario dos veces; es una garantía a nivel de campaña, no un reintento seguro de una solicitud. Aquí, pon tu ID de correlación en metadata o tags, y usa la cabecera Idempotency-Key para que una solicitud reintentada sea segura.
- Las plantillas almacenadas se portan directamente. TemplateID + Variables de Mailjet se mapean a nuestro campo template (referenciado por ID o slug) con valores en template.parameters. Consulta enviar con una plantilla. La lógica de plantillas más allá de la sustitución de variables también se porta: los condicionales y bucles TemplateLanguage de Mailjet pasan a ser {% if %} y {% for %} de Liquid en nuestra plantilla.
- Los adjuntos se portan directamente. El Base64Content de Mailjet es nuestro content en base64, y InlinedAttachments + ContentID pasan a ser entradas attachments con content_id. Consulta adjuntos.
Exportar supresiones
Mailjet guarda las direcciones inalcanzables y no deseadas en su blocklist (rebotes duros/suaves y envíos bloqueados) y rastrea las señales de spam y cancelación de suscripción por separado. Exporta las direcciones bloqueadas y rebotadas desde las páginas de estadísticas de contactos de Mailjet, o consúltalas a través de la API de gestión de contactos, y pasa la lista por el bucle de importación. Si envías correo de marketing, transfiere también los contactos marcados como dados de baja para que esas preferencias sobrevivan a la migración.
Traducir eventos de webhook
El Event API de Mailjet envía un disparo por tipo de evento. La correspondencia con nuestro vocabulario de eventos:
| Resultado | Mailjet | Bird |
|---|---|---|
| Aceptado/procesado | (ninguno) | email.accepted → email.processed |
| Entregado | sent | email.delivered |
| Rebote permanente | bounce | email.bounced / email.out_of_band_bounce |
| Bloqueado | blocked | email.rejected |
| Queja de spam | spam | email.complained |
| Apertura | open | email.opened |
| Clic | click | email.clicked |
| Cancelación de suscripción | unsub | email.unsubscribed / email.list_unsubscribed |
Dos diferencias que conviene manejar en el código:
- Informamos las etapas previas a la entrega de forma explícita. El sent de Mailjet se dispara cuando el servidor de correo del destinatario acepta el mensaje, lo cual corresponde a nuestro email.delivered. Nosotros también emitimos email.accepted y email.processed antes, para que veas el progreso del envío antes de la confirmación de entrega. No trates esos eventos anteriores como entrega.
- Los eventos tienen alcance por destinatario. Mailjet asocia los eventos por MessageID. Nuestros eventos de entrega incluyen recipient_id junto con email_id, así que un envío a múltiples destinatarios genera un flujo de eventos por destinatario. Firmamos las entregas según la especificación Standard Webhooks. Consulta Webhooks y eventos para la verificación.
Transición
Sigue los pasos de 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 estás redirigiendo
- Webhooks y eventos: configuración de endpoints y verificación con Standard Webhooks
- Sandbox de pruebas: prueba 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 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