# Migrar desde Brevo

Esta página mapea el payload de envío transaccional, las listas de bloqueo y los webhooks de Brevo a Bird. Sigue la [guía principal de migración](/docs/guides/email/migrate) en orden y usa estas correspondencias para los pasos 1, 3 y 4.

Brevo guarda las supresiones en dos lugares independientes, uno para correo transaccional y otro para marketing. Lee [Exportar supresiones](#exportar-supresiones) antes de planificar el paso 3: exportar una lista y no la otra es el error típico de esta migración.

## 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.

```text
I am moving an email integration from Brevo 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/brevo.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 Brevo usage in this repository before you change anything: calls to /v3/smtp/email and any SDK wrappers around them, whether I send with templateId or with htmlContent, 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 Brevo 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. Brevo holds these in two separate places and I need both: GET /v3/smtp/blockedContacts for transactional blocks and unsubscribes, and the marketing blocklist, which lives on the contact records as emailBlacklisted rather than on a suppression endpoint. Page through both. 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. Brevo's webhook security page documents credentials I configure on the endpoint rather than a payload signature, so check what my endpoint actually relies on today: if it is a username and password in the webhook URL, take them out and tell me to rotate that pair rather than reusing it, because a credential that has lived in a URL should be treated as exposed. Bird signs every delivery instead. 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 Brevo path is a separate step that comes later: ask me again and wait for me to reply with the words retire the Brevo 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 /v3/smtp/email` de Brevo y nuestro [`POST /v1/email/messages`](/docs/api/reference/create-email-message) tienen una estructura similar. La mayor parte del trabajo consiste en convertir los objetos de dirección de Brevo en cadenas simples.

| Qué hace                   | Brevo                                           | Bird                                                                |
| -------------------------- | ----------------------------------------------- | ------------------------------------------------------------------- |
| Autenticación              | Encabezado `api-key`                            | `Authorization: Bearer`                                             |
| Remitente                  | `sender` (`{email, name}`)                      | `from`                                                              |
| Destinatarios              | `to` / `cc` / `bcc` (arrays de `{email, name}`) | `to` / `cc` / `bcc` (arrays de direcciones)                         |
| Asunto                     | `subject`                                       | `subject`                                                           |
| Cuerpo                     | `htmlContent` / `textContent`                   | `html` / `text` (al menos uno)                                      |
| Reply-to                   | `replyTo` (`{email, name}`)                     | `reply_to` (array)                                                  |
| Encabezados personalizados | `headers` (claves en Title-Case)                | `headers` (objeto string → string)                                  |
| Etiquetas filtrables       | `tags` (array de strings)                       | Pares `tags`: `{name, value}`                                       |
| Plantilla almacenada       | `templateId` + `params`                         | `template` + `template.parameters`                                  |
| Adjuntos                   | `attachment` (`url` o base64 `content`)         | `attachments` (solo base64, ver más abajo)                          |
| Programación               | `scheduledAt`                                   | `scheduled_at`                                                      |
| Identificador de lote      | `batchId`                                       | (sin equivalente, ver más abajo)                                    |
| Copia por destinatario     | `messageVersions`                               | un envío por versión, o un [batch](/docs/guides/email/sending-bulk) |
| Categoría                  | (ninguna)                                       | `category`: `marketing` (por defecto) o `transactional`             |

Los límites y valores por defecto de nuestros campos (cantidad de destinatarios, límites de tags y metadatos) están en [Envío de correo](/docs/guides/email/sending-email).

Notas de portabilidad:

- **Las direcciones son objetos allí y cadenas aquí.** `{"email": "a@x.com", "name": "A"}` se convierte en `"A <a@x.com>"` o simplemente `"a@x.com"`. La misma conversión aplica a `sender` y `replyTo`.
- **Los `tags` son cadenas simples. Nuestros tags son pares.** Un tag como `"welcome"` se convierte en `{"name": "category", "value": "welcome"}`. Elige un `name` estable para que tus dashboards filtren igual que lo hacían las estadísticas de tags en Brevo.
- **`params` son datos de plantilla, no contexto de ida y vuelta.** Se convierten en `template.parameters`. Si también los usabas para pasar tus propios identificadores hasta los eventos, muévelos a [`metadata`](/docs/guides/email/sending-email), que devolvemos en cada evento de webhook junto con `email_id`/`recipient_id`.
- **`messageVersions` no tiene equivalente en una sola llamada.** Cada versión es un conjunto de destinatarios y payload distinto, así que se convierte en su propio envío o en una entrada de un [batch](/docs/guides/email/sending-bulk).
- **`batchId` no tiene equivalente.** `batchId` de Brevo agrupa mensajes programados para que puedas cancelarlos o reprogramarlos como conjunto. Los envíos programados aquí se gestionan individualmente por su id de mensaje; no hay un identificador de grupo que pasar o contra el cual cancelar.
- **No se admiten adjuntos por URL.** Brevo acepta una entrada `attachment` como URL para que él la descargue. Descarga el archivo tú mismo y envíalo codificado en base64; consulta [adjuntos](/docs/guides/email/attachments).

## Exportar supresiones

Brevo divide las supresiones en dos sistemas que no comparten endpoint ni esquema de paginación, y una migración que extrae solo el primero pierde silenciosamente todas las cancelaciones de suscripción de marketing:

- **Bloqueos y cancelaciones de suscripción transaccionales**: `GET /v3/smtp/blockedContacts`, paginado (50 por página por defecto, 100 como máximo), cada entrada con el motivo del bloqueo.
- **Lista de bloqueo de marketing**: no es un endpoint de supresiones en absoluto. Reside en el registro de contacto como `emailBlacklisted`, así que pagina `GET /v3/contacts` (hasta 1000 por página con `offset`) y conserva los contactos donde esa bandera sea true.

Ejecuta ambos a través del [bucle de importación](/docs/guides/email/migrate#3-import-suppressions). Los motivos de bloqueo de Brevo se mapean a nuestros motivos `hard_bounce`, `complaint` y `manual`; [Supresiones](/docs/guides/email/suppressions) tiene la taxonomía completa.

## Traducir eventos de webhook

| Resultado                  | Brevo                       | Bird                                             |
| -------------------------- | --------------------------- | ------------------------------------------------ |
| Aceptado/procesado         | `request`                   | `email.accepted` → `email.processed`             |
| Entregado                  | `delivered`                 | `email.delivered`                                |
| Fallo temporal             | `deferred` / `soft_bounce`  | `email.deferred`                                 |
| Rebote permanente          | `hard_bounce`               | `email.bounced` / `email.out_of_band_bounce`     |
| Queja de spam              | `spam`                      | `email.complained`                               |
| Bloqueado/suprimido        | `blocked` / `invalid_email` | `email.rejected`                                 |
| Apertura                   | `opened` / `unique_opened`  | `email.opened`                                   |
| Clic                       | `click`                     | `email.clicked`                                  |
| Cancelación de suscripción | `unsubscribed`              | `email.unsubscribed` / `email.list_unsubscribed` |

Dos diferencias determinan cuánto cambia tu handler.

**Revisa de qué depende tu endpoint hoy antes de portarlo.** La página de seguridad de webhooks de Brevo documenta las credenciales que configuras en el endpoint: un nombre de usuario y contraseña añadidos a la URL como `https://username:password@example.com/`, un bearer token, encabezados de solicitud personalizados y sus rangos de IP. Nosotros firmamos cada entrega según el esquema HMAC de [Standard Webhooks](https://www.standardwebhooks.com), así que la verificación pasa de algo que el emisor porta a algo que tu handler calcula. Si tu endpoint actual tiene credenciales en la URL, elimínalas y rota ese par en lugar de reutilizarlo: una credencial que ha vivido en una URL ha llegado a logs de acceso, exportaciones de configuración y una consola de proveedor. La receta está en [Webhooks y eventos](/docs/guides/webhooks).

**Brevo distingue aperturas y clics de sus variantes únicas. Nosotros no.** `opened` y `unique_opened` llegan ambos como `email.opened`, así que un handler que contaba solo la variante única necesita deduplicar sobre `recipient_id` directamente. Nuestros eventos de entrega tienen alcance por destinatario, por lo que un envío a tres destinatarios produce tres resultados de entrega en lugar de uno.

## Migración final

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) en la guía principal. Ambos pasos 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 con Standard Webhooks
- [Sandbox de pruebas](/docs/guides/email/testing-sandbox): prueba de humo de la nueva integración antes de la migración final
- [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)
