Bird

Migrar desde Amazon SES

Esta página relaciona la llamada SendEmail de SES v2, la lista de supresión a nivel de cuenta y las notificaciones de eventos SNS con Bird. Sigue la guía principal de migración en orden y usa estas correspondencias para los pasos 1, 3 y 4.

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 Amazon SES 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/ses.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 SES usage in this repository and its infrastructure before you change anything: the SendEmail and SendRawEmail call sites through the AWS SDK or CLI, the configuration sets they name, the SNS topics or EventBridge rules carrying my events, the handler subscribed to them, and every identity I send from. Say which of these live in infrastructure code rather than application code, because those change by a different route.
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 the SES DKIM CNAMEs exactly as they are: Bird's DKIM record uses its own selector, so the two coexist 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 account-level suppression list from SES 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 from GET /v2/email/suppressed-destinations, paginating with NextToken to the end, and keep both the BOUNCE and COMPLAINT reasons. 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 replace the event plumbing. Bird posts signed webhooks straight to an endpoint, so the SNS topic, the subscription-confirmation handshake, and the message-envelope unwrapping all go away rather than being ported: my handler reads the event body directly and verifies it per Standard Webhooks. See https://bird.com/docs/guides/webhooks.md and https://bird.com/docs/guides/email/events.md. Tell me which SNS or EventBridge resources become unused, but do not delete any of them.
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 SES path is a separate step that comes later: ask me again and wait for me to reply with the words retire the SES 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.

Relacionar la llamada de envío

SES divide un envío entre Destination, Content y la configuración de configuration sets. Nuestro POST /v1/email/messages es un solo payload plano:
FunciónSES (SendEmail v2)Bird
RemitenteFromEmailAddressfrom
DestinatariosDestination.*Addressesto / cc / bcc (arrays)
AsuntoContent.Simple.Subjectsubject
CuerpoContent.Simple.Body.Html/Texthtml / text (al menos uno)
Reply-toReplyToAddressesreply_to (array)
Headers personalizadosContent.Simple.Headersheaders (objeto string → string)
Etiquetas filtrablesEmailTagstags: pares {name, value}
Contexto de ida y vuelta(ninguno)metadata: JSON arbitrario
Plantilla almacenadaContent.Templatetemplate + template.parameters
Seguimiento de aperturas/clicsconfiguration settrack_opens / track_clicks (por defecto true)
Pool de IPsdedicated IP pool (config set)ip_pool_id (ipp_... o ipp_shared)
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 email.
Notas de migración:
  • Los configuration sets se disuelven en campos por mensaje. Seguimiento, pool de IPs y enrutamiento de eventos eran responsabilidad de los configuration sets en SES. Aquí, los dos primeros son campos del payload y el enrutamiento de eventos es una suscripción de webhook.
  • La autenticación cambia de SigV4 a un bearer token. No hay firma de solicitudes; solo un header Authorization: Bearer bk_... simple. Elimina la cadena de credenciales SDK de AWS de esta ruta de código.
  • Las plantillas de SES se migran a plantillas almacenadas. Content.Template (nombre de plantilla más TemplateData) se corresponde con nuestro campo template con valores en template.parameters. Consulta enviar con una plantilla.
  • Content.Raw (MIME) no tiene equivalente. Construimos el mensaje a partir de campos estructurados. Si ensamblas MIME sin procesar para adjuntar archivos, envíalos con nuestro array de attachments (base64 content por archivo, content_id para imágenes inline).
  • El sandbox de SES ≠ el sandbox de Bird. El sandbox de SES restringe a quién puedes enviar. Nuestro sandbox de correo es un simulador con direcciones mágicas: sin listas de permitidos y nada se entrega.

Exportar supresiones

Exporta la lista de supresión a nivel de cuenta y pásala por el bucle de importación:
  • GET /v2/email/suppressed-destinations (pagina con NextToken; cada entrada tiene BOUNCE o COMPLAINT como razón)

Traducir eventos de webhook

SES publica eventos a través de SNS o EventBridge. Nosotros enviamos webhooks firmados por POST directamente, así que el topic de SNS, el handshake de confirmación de suscripción y el desempaquetado del sobre del mensaje desaparecen. Los nombres de eventos se corresponden así:
ResultadoSESBird
Aceptado/procesadoSendemail.acceptedemail.processed
EntregadoDeliveryemail.delivered
Fallo temporalDeliveryDelayemail.deferred
Rebote permanenteBounceemail.bounced / email.out_of_band_bounce
Queja de spamComplaintemail.complained
Bloqueado/suprimido(ninguno)email.rejected
AperturaOpenemail.opened
ClicClickemail.clicked
Cancelación de suscripciónSubscriptionemail.list_unsubscribed
email.rejected es nuevo respecto a SES: reportamos los destinatarios suprimidos de forma visible (estado rejected, rejection_reason: recipient_suppressed) en lugar de contarlos en el ciclo de envío y rebote. Agrega un handler para este evento.
En lugar de la verificación de mensajes SNS, firmamos según la especificación de Standard Webhooks, con headers HMAC en la entrega misma. La receta de verificación está en Webhooks y eventos.

Puesta en producción

Sigue los pasos de dominios y DNS y la prueba de humo del sandbox en la guía principal. Ambos son independientes del proveedor. Una nota específica de SES para el paso de DNS: los CNAMEs DKIM de SES permanecen en su lugar durante la transición. Nuestro registro TXT DKIM usa su propio selector, así que ambos coexisten.

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