<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ensamblador</title>
    <description>The latest articles on DEV Community by ensamblador (@ensamblador).</description>
    <link>https://dev.to/ensamblador</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg</url>
      <title>DEV Community: ensamblador</title>
      <link>https://dev.to/ensamblador</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ensamblador"/>
    <language>en</language>
    <item>
      <title>Respondiendo DMs de X en Amazon Connect Chat</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Fri, 03 Apr 2026 17:11:39 +0000</pubDate>
      <link>https://dev.to/ensamblador/respondiendo-dms-de-x-en-amazon-connect-chat-40b5</link>
      <guid>https://dev.to/ensamblador/respondiendo-dms-de-x-en-amazon-connect-chat-40b5</guid>
      <description>&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;em&gt;Aprende cómo conectar los Mensajes Directos de X (Twitter) con Amazon Connect Chat para una atención al cliente fluida. Esta guía paso a paso cubre la arquitectura completa usando AWS CDK, AWS Lambda, Amazon API Gateway, Amazon DynamoDB y Amazon Connect. Desde recibir DMs de clientes hasta enrutarlos a agentes, reenviar respuestas de agentes a X y manejar archivos adjuntos en ambas direcciones — todo con gestión automática de sesiones, validación CRC de webhooks y caché de perfiles de usuario vía el SDK de Tweepy.&lt;/em&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0on530id1p6mj2afzu9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0on530id1p6mj2afzu9.gif" alt="Demo" width="600" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tus clientes ya están en X. Siguen tu marca, interactúan con tus publicaciones, y cuando necesitan ayuda — envían un DM. Si tu equipo de soporte tiene que alternar entre X y su herramienta de contact center, estás perdiendo tiempo y contexto.&lt;/p&gt;

&lt;p&gt;En este blog, aprenderás cómo conectar los Mensajes Directos de X directamente a Amazon Connect Chat, para que tus agentes manejen conversaciones de X desde el mismo workspace que usan para todos los demás canales. Sin cambiar de app, sin copiar y pegar, sin mensajes perdidos.&lt;/p&gt;

&lt;p&gt;Revisa el código en &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Qué vas a construir
&lt;/h2&gt;

&lt;p&gt;Un puente de mensajería bidireccional entre DMs de X y Amazon Connect que:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Recibe DMs entrantes de X vía el webhook del Account Activity API y los enruta a Amazon Connect Chat&lt;/li&gt;
&lt;li&gt;Reenvía las respuestas de los agentes desde Amazon Connect de vuelta a X a través del SDK de Tweepy&lt;/li&gt;
&lt;li&gt;Gestiona sesiones de chat automáticamente — creando nuevas, reutilizando activas y limpiando las expiradas&lt;/li&gt;
&lt;li&gt;Cachea perfiles de usuario de X (nombre, username, imagen de perfil) en DynamoDB para reducir llamadas al API&lt;/li&gt;
&lt;li&gt;Maneja archivos adjuntos en ambas direcciones — imágenes, videos y GIFs de clientes, e imágenes y videos de agentes&lt;/li&gt;
&lt;li&gt;Previene loops echo filtrando mensajes enviados por tu propia cuenta de X&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El resultado final: los agentes ven las conversaciones de X como contactos de chat regulares en su workspace de Amazon Connect, con el nombre y la información de perfil del cliente.&lt;/p&gt;
&lt;h2&gt;
  
  
  Arquitectura
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcyklp9rw2ixwrah7cnrs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcyklp9rw2ixwrah7cnrs.gif" alt="Diagrama de Arquitectura" width="996" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Así funciona el flujo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Un cliente envía un DM en X. El Account Activity API entrega el evento webhook a un endpoint de API Gateway&lt;/li&gt;
&lt;li&gt;La Lambda del Inbound Handler valida el webhook (CRC challenge), parsea el mensaje y busca o crea una sesión de Amazon Connect Chat&lt;/li&gt;
&lt;li&gt;El perfil de X del cliente (si no está presente en el request) se obtiene vía el SDK de Tweepy y se cachea en DynamoDB&lt;/li&gt;
&lt;li&gt;Los mensajes de texto y archivos adjuntos se envían a la sesión de Connect Chat vía el Participant API&lt;/li&gt;
&lt;li&gt;Cuando un agente responde, Amazon Connect publica el evento a un topic SNS vía contact streaming&lt;/li&gt;
&lt;li&gt;La Lambda del Outbound Handler recibe el evento SNS, busca el ID de usuario de X del cliente y envía la respuesta como DM a través del SDK de Tweepy&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Entrante: X → Amazon Connect
&lt;/h2&gt;

&lt;p&gt;Cuando un cliente envía un DM a tu cuenta business de X, el flujo entrante maneja todo, desde la validación del webhook hasta la entrega del mensaje.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Validación CRC del Webhook
&lt;/h3&gt;

&lt;p&gt;X usa un Challenge-Response Check (CRC) para verificar la propiedad del webhook — esto es fundamentalmente diferente del enfoque de Meta usado en las integraciones de Instagram y Facebook Messenger. En lugar de comparar un string secreto compartido, X envía un &lt;code&gt;crc_token&lt;/code&gt; que debe ser hasheado con tu Consumer Secret usando HMAC-SHA256:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_crc_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crc_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;crc_token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;encoded_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sha256=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;encoded_hash&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;X envía este challenge tanto durante el registro inicial del webhook como periódicamente después para re-validar. La Lambda lo maneja automáticamente en cada solicitud GET.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Parseo de Mensajes y Prevención de Echo
&lt;/h3&gt;

&lt;p&gt;Para solicitudes POST (eventos DM reales), la clase &lt;code&gt;XService&lt;/code&gt; parsea el payload &lt;code&gt;direct_message_events&lt;/code&gt;. Cada evento contiene el ID del remitente, ID del destinatario, contenido de texto y cualquier adjunto multimedia:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;XMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;message_create&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message_create&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;message_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message_data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sender_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipient_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;target&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recipient_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Parsear adjunto si está presente
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;media&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;media&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;media_url_https&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# photo, animated_gif, video
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El servicio filtra mensajes enviados por tu propio ID de cuenta de X para prevenir loops echo — cuando tu cuenta envía una respuesta, X también la entrega como evento webhook.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Obtención y Caché de Perfiles de Usuario
&lt;/h3&gt;

&lt;p&gt;Los payloads del webhook de X incluyen datos de perfil de usuario inline en un diccionario &lt;code&gt;users&lt;/code&gt;, que el servicio extrae primero. Para perfiles faltantes, recurre a una búsqueda de tres niveles:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Verificar caché en memoria primero
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Verificar tabla de usuarios en DynamoDB
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;users_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TableService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;db_profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;

    &lt;span class="c1"&gt;# Obtener del X API vía Tweepy como último recurso
&lt;/span&gt;    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;consumer_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;consumer_key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;consumer_secret&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;access_token_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;access_token_secret&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;profile_image_url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="c1"&gt;# ... cachear en DynamoDB con TTL de 7 días
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El perfil incluye &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt; y &lt;code&gt;profile_image_url&lt;/code&gt;. Los perfiles se cachean en una tabla DynamoDB con un TTL de 7 días, así que las conversaciones repetidas se saltan la llamada al API.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Gestión de Sesiones
&lt;/h3&gt;

&lt;p&gt;El handler verifica en DynamoDB si existe una sesión de chat activa usando el ID de usuario de X del remitente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Si existe una sesión, envía el mensaje usando el &lt;code&gt;connectionToken&lt;/code&gt; almacenado. Si el token expiró (AccessDeniedException), crea automáticamente una nueva sesión.&lt;/li&gt;
&lt;li&gt;Si no existe sesión, llama a &lt;code&gt;StartChatContact&lt;/code&gt; para crear un nuevo Amazon Connect Chat, inicia contact streaming al topic SNS, crea una conexión de participante y almacena todo en DynamoDB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los atributos de contacto incluyen el nombre del canal ("X"), el ID del cliente y el nombre de display del cliente — facilitando identificar el canal de origen en Contact Flows y enrutamiento de agentes.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Manejo de Archivos Adjuntos (Entrante)
&lt;/h3&gt;

&lt;p&gt;Cuando un cliente envía una imagen, GIF o video, el handler lo descarga del CDN de X y lo sube a la sesión de Connect Chat. Las URLs de media de X vienen en dos variantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pbs.twimg.com&lt;/code&gt; — accesible públicamente, descarga directa&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ton.twitter.com&lt;/code&gt; — requiere autenticación OAuth 1.0a (usando &lt;code&gt;requests_oauthlib&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La carga usa el flujo de tres pasos del Participant API: &lt;code&gt;start_attachment_upload&lt;/code&gt; → PUT a URL pre-firmada → &lt;code&gt;complete_attachment_upload&lt;/code&gt;. Si algo falla, el handler envía la URL del media como mensaje de texto.&lt;/p&gt;

&lt;p&gt;Para adjuntos que incluyen un caption, el texto se limpia eliminando el enlace &lt;code&gt;t.co&lt;/code&gt; que X agrega automáticamente al cuerpo del mensaje.&lt;/p&gt;
&lt;h2&gt;
  
  
  Saliente: Amazon Connect → X
&lt;/h2&gt;

&lt;p&gt;Cuando un agente responde desde el workspace de Amazon Connect, el flujo saliente entrega el mensaje de vuelta a X.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Eventos de Streaming vía SNS
&lt;/h3&gt;

&lt;p&gt;Amazon Connect publica eventos de streaming de chat a un topic SNS. La Lambda del Outbound Handler se suscribe a este topic y procesa tres tipos de eventos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MESSAGE&lt;/code&gt; — mensajes de texto del agente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ATTACHMENT&lt;/code&gt; — archivos adjuntos enviados por el agente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EVENT&lt;/code&gt; — eventos de unión/salida de participantes y fin de chat&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los mensajes del rol &lt;code&gt;CUSTOMER&lt;/code&gt; se omiten para evitar procesar los propios mensajes del cliente nuevamente.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Envío de Mensajes de Texto
&lt;/h3&gt;

&lt;p&gt;Para mensajes de texto con visibilidad &lt;code&gt;CUSTOMER&lt;/code&gt; o &lt;code&gt;ALL&lt;/code&gt;, el handler busca el ID de usuario de X del cliente en DynamoDB y envía la respuesta vía el API v2 de Tweepy:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_x_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;consumer_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;consumer_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;consumer_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;access_token_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_direct_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;participant_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Envío de Archivos Adjuntos
&lt;/h3&gt;

&lt;p&gt;Cuando un agente envía un archivo desde el widget de Connect Chat, el handler obtiene una URL firmada del adjunto, lo descarga y lo sube a X vía el endpoint de media upload v1.1 (OAuth 1.0a). El media ID se usa luego para enviar un DM con el adjunto:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tipo MIME&lt;/th&gt;
&lt;th&gt;Categoría de media X&lt;/th&gt;
&lt;th&gt;Método de carga&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;image/jpeg&lt;/code&gt;, &lt;code&gt;image/png&lt;/code&gt;, &lt;code&gt;image/webp&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dm_image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;media_upload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image/gif&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dm_gif&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chunked_upload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video/mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dm_video&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chunked_upload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;todo lo demás&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Enviado como enlace de texto&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Los DMs de X solo soportan imágenes y videos como media nativo. Los tipos no soportados (PDFs, documentos, etc.) se envían como URLs en texto plano para que el cliente aún tenga acceso al contenido.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Limpieza de Sesiones
&lt;/h3&gt;

&lt;p&gt;Cuando un participante sale o el chat termina, el handler elimina el registro de conexión de DynamoDB para que el próximo mensaje entrante inicie una sesión nueva.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tipos de Mensajes Soportados
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dirección&lt;/th&gt;
&lt;th&gt;Texto&lt;/th&gt;
&lt;th&gt;Imágenes&lt;/th&gt;
&lt;th&gt;Videos&lt;/th&gt;
&lt;th&gt;GIFs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entrante (cliente → agente)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saliente (agente → cliente)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Los tipos de media no soportados (PDFs, documentos, etc.) se envían como enlaces de texto plano en la dirección saliente.&lt;/p&gt;
&lt;h2&gt;
  
  
  Qué se Despliega
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Recurso&lt;/th&gt;
&lt;th&gt;Servicio&lt;/th&gt;
&lt;th&gt;Propósito&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Endpoint &lt;code&gt;/webhooks&lt;/code&gt; (GET &amp;amp; POST)&lt;/td&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;Recibe desafíos CRC de X (GET) y eventos DM entrantes (POST)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Procesa eventos DM de X y los enruta a Amazon Connect Chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Envía respuestas de agentes de vuelta a X como DMs vía el SDK de Tweepy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tabla Active Connections&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Rastrea sesiones de chat abiertas (&lt;code&gt;contactId&lt;/code&gt; PK, &lt;code&gt;userId&lt;/code&gt; GSI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tabla X Users&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Cachea perfiles de usuario de X (expiración por TTL, 7 días)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topic &lt;code&gt;messages_out&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;SNS&lt;/td&gt;
&lt;td&gt;Entrega eventos de streaming de Amazon Connect al Outbound Handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x-dm-credentials&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;Almacena credenciales OAuth 1.0a del X API (Consumer Key, Consumer Secret, Access Token, Access Token Secret)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/x/dm/config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Contiene instance ID de Connect, contact flow ID e ID de cuenta de X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/x/dm/webhook/url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Almacena la URL del callback de API Gateway desplegado&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Estimación de Costos
&lt;/h2&gt;

&lt;p&gt;Escenario de ejemplo: 1,000 conversaciones por mes, promediando 10 mensajes cada una (5 entrantes + 5 salientes), totalizando 10,000 mensajes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Componente&lt;/th&gt;
&lt;th&gt;Costo Mensual Estimado&lt;/th&gt;
&lt;th&gt;Notas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infraestructura (API GW, Lambda, DynamoDB, SNS, Secrets Manager)&lt;/td&gt;
&lt;td&gt;~$0.71&lt;/td&gt;
&lt;td&gt;Despreciable a esta escala&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Entrante)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Saliente)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X API — DMs Salientes&lt;/td&gt;
&lt;td&gt;~$50.00&lt;/td&gt;
&lt;td&gt;5,000 envíos de DM × ~$0.01/request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$90.71&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;El X API usa precios de pago por uso basados en créditos. El costo por endpoint mostrado arriba es aproximado — las tarifas reales se muestran en la &lt;a href="https://console.x.com/" rel="noopener noreferrer"&gt;Consola de Desarrollador de X&lt;/a&gt; y pueden cambiar. Consulta los &lt;a href="https://aws.amazon.com/connect/pricing/" rel="noopener noreferrer"&gt;precios de Amazon Connect&lt;/a&gt; y los &lt;a href="https://developer.x.com/en/products/twitter-api" rel="noopener noreferrer"&gt;precios del X API&lt;/a&gt; para tarifas actuales.&lt;/p&gt;

&lt;p&gt;Para reducir costos de Connect Chat en conversaciones de alto volumen, considera agregar una &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/whatsapp-eum-connect-chat" rel="noopener noreferrer"&gt;capa de buffering de mensajes&lt;/a&gt; para agregar mensajes consecutivos rápidos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerrequisitos de Despliegue
&lt;/h2&gt;

&lt;p&gt;Antes de comenzar necesitarás:&lt;/p&gt;
&lt;h3&gt;
  
  
  Cuenta de Desarrollador de X y Credenciales API
&lt;/h3&gt;

&lt;p&gt;Necesitas una cuenta de desarrollador de X con al menos el tier Pay-Per-Use, y cuatro credenciales OAuth 1.0a (Consumer Key, Consumer Secret, Access Token, Access Token Secret).&lt;/p&gt;

&lt;p&gt;Consulta la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/x_setup.md" rel="noopener noreferrer"&gt;Guía de Configuración de X&lt;/a&gt; para instrucciones detalladas paso a paso sobre cómo crear tu app, configurar permisos y generar credenciales.&lt;/p&gt;

&lt;p&gt;⚠️ Importante: El tier gratuito no incluye entrega de DMs basada en webhooks. Necesitas el tier Pay-Per-Use.&lt;/p&gt;
&lt;h3&gt;
  
  
  Una Instancia de Amazon Connect
&lt;/h3&gt;

&lt;p&gt;Necesitas una instancia de Amazon Connect. Si aún no tienes una, puedes &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;seguir esta guía&lt;/a&gt; para crear una.&lt;/p&gt;

&lt;p&gt;Necesitarás el &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; de tu instancia. Lo puedes encontrar en la consola de Amazon Connect o en el ARN de la instancia:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Un Flujo de Chat para Manejar Mensajes
&lt;/h3&gt;

&lt;p&gt;Crea o ten listo el flujo de contacto que define la experiencia del usuario. &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/create-contact-flow.html" rel="noopener noreferrer"&gt;Sigue esta guía&lt;/a&gt; para crear un Inbound Contact Flow. El más sencillo funcionará.&lt;/p&gt;

&lt;p&gt;Recuerda publicar el flujo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" alt="Flujo Simple" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Toma nota del &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; y &lt;strong&gt;CONTACT_FLOW_ID&lt;/strong&gt; en la pestaña de Detalles. Los valores están en el ARN del flujo:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID/contact-flow/CONTACT_FLOW_ID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(consulta los &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_connect.md" rel="noopener noreferrer"&gt;Prerrequisitos de Amazon Connect&lt;/a&gt; para más detalles)&lt;/p&gt;
&lt;h2&gt;
  
  
  Despliegue con AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Despliega en la misma región donde tu instancia de Amazon Connect está configurada.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Clona el repositorio y navega al proyecto
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-amazon-connect-social-integration.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-amazon-connect-social-integration/x-dm-connect-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Despliega con CDK
&lt;/h3&gt;

&lt;p&gt;Sigue las instrucciones en la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;Guía de Despliegue CDK&lt;/a&gt; para configuración del entorno y comandos de despliegue.&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuración Post-despliegue
&lt;/h2&gt;

&lt;p&gt;Después del despliegue, se necesitan tres pasos de configuración:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Actualizar Credenciales del X API&lt;/strong&gt; — El stack crea un secreto en Secrets Manager llamado &lt;code&gt;x-dm-credentials&lt;/code&gt; con valores placeholder. Actualízalo con tu Consumer Key, Consumer Secret, Access Token y Access Token Secret reales.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Actualizar Configuración SSM&lt;/strong&gt; — Actualiza el parámetro SSM &lt;code&gt;/x/dm/config&lt;/code&gt; con tu &lt;code&gt;instance_id&lt;/code&gt; de Amazon Connect, &lt;code&gt;contact_flow_id&lt;/code&gt; y el &lt;code&gt;x_account_id&lt;/code&gt; numérico de tu cuenta de X.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Registrar el Webhook y Suscribirse&lt;/strong&gt; — Registra la URL de tu API Gateway desplegado con el Account Activity API de X y suscribe tu cuenta business para recibir eventos DM. El Inbound Handler responde a los desafíos CRC automáticamente.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para instrucciones detalladas de cada paso, incluyendo cómo encontrar tu &lt;code&gt;x_account_id&lt;/code&gt; y registrar el webhook, consulta el &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/tree/main/x-dm-connect-chat" rel="noopener noreferrer"&gt;README del proyecto&lt;/a&gt; y la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/x_setup.md" rel="noopener noreferrer"&gt;Guía de Configuración de X&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pruebas
&lt;/h2&gt;

&lt;p&gt;Ve a tu instancia de Amazon Connect y &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/launch-ccp.html" rel="noopener noreferrer"&gt;abre el Contact Control Panel (CCP)&lt;/a&gt;.&lt;/p&gt;





&lt;p&gt;Prueba estos escenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Envía un DM a tu cuenta business de X desde otra cuenta de X — debería aparecer como un nuevo contacto de chat en el CCP&lt;/li&gt;
&lt;li&gt;Responde desde el CCP — la respuesta debería llegar a los DMs de X del cliente&lt;/li&gt;
&lt;li&gt;Envía una imagen desde X — debería aparecer como un adjunto de imagen en el chat del agente&lt;/li&gt;
&lt;li&gt;Desde el lado del agente, envía una imagen — debería aparecer en los DMs de X del cliente&lt;/li&gt;
&lt;li&gt;Intenta enviar un documento desde el lado del agente — debería llegar como enlace en los DMs del cliente&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Consideraciones Importantes sobre X
&lt;/h2&gt;
&lt;h3&gt;
  
  
  DMs Encriptados
&lt;/h3&gt;

&lt;p&gt;X soporta Mensajes Directos con encriptación de extremo a extremo (E2EE). Sin embargo, &lt;strong&gt;los DMs encriptados no son accesibles vía el X API&lt;/strong&gt;. Esta integración solo procesa DMs estándar (no encriptados). Si una conversación está encriptada, el webhook no recibirá esos eventos de mensaje.&lt;/p&gt;
&lt;h3&gt;
  
  
  Tier Pay-Per-Use
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;El tier &lt;strong&gt;Pay-Per-Use&lt;/strong&gt; es requerido para acceso al Account Activity API. El tier gratuito no incluye entrega de DMs basada en webhooks.&lt;/li&gt;
&lt;li&gt;Revisa los &lt;a href="https://developer.x.com/en/products/twitter-api" rel="noopener noreferrer"&gt;precios del X API&lt;/a&gt; para detalles y costos actuales.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Re-validación CRC
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;X periódicamente re-envía desafíos CRC para verificar que tu webhook sigue siendo válido. El Inbound Handler maneja esto automáticamente, pero asegúrate de que las credenciales en Secrets Manager permanezcan válidas y la función Lambda siga desplegada.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Credenciales OAuth 1.0a
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Las cuatro credenciales (Consumer Key, Consumer Secret, Access Token, Access Token Secret) deben permanecer válidas. Si regeneras cualquier credencial en el Portal de Desarrollador de X, actualiza el secreto en Secrets Manager inmediatamente.&lt;/li&gt;
&lt;li&gt;El Access Token y Access Token Secret están vinculados a la cuenta de usuario de X específica que posee la app. Asegúrate de que sea la cuenta business que debe recibir y enviar DMs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Límites de Tasa
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;El X API aplica límites de tasa en los endpoints de DM. El Account Activity API tiene sus propios límites en registros de webhooks y validaciones CRC.&lt;/li&gt;
&lt;li&gt;Monitorea tu uso en el dashboard del &lt;a href="https://developer.x.com/" rel="noopener noreferrer"&gt;Portal de Desarrollador de X&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Próximos Pasos
&lt;/h2&gt;

&lt;p&gt;Esta solución maneja el flujo principal de mensajería X DM-a-Connect. Algunas ideas para extenderla:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usar Amazon Bedrock para analizar imágenes entrantes y dar contexto a los agentes antes de que respondan&lt;/li&gt;
&lt;li&gt;Usar &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/agentic-self-service.html" rel="noopener noreferrer"&gt;Amazon Connect AI Agents&lt;/a&gt; para autoservicio agéntico, permitiendo a los clientes resolver problemas comunes sin esperar a un agente humano&lt;/li&gt;
&lt;li&gt;Combinar con la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/tree/main/instagram-dm-connect-chat" rel="noopener noreferrer"&gt;integración de Instagram DM&lt;/a&gt; y la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/tree/main/facebook-messenger-connect-chat" rel="noopener noreferrer"&gt;integración de Facebook Messenger&lt;/a&gt; para manejar todos los canales sociales desde una sola instancia de Amazon Connect&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Aprovechar Amazon Connect Customer Profiles
&lt;/h3&gt;

&lt;p&gt;Esta solución ya obtiene datos del perfil de X (nombre, username, imagen de perfil) y los pasa como atributos de contacto. Puedes ir más allá integrando con &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles.html" rel="noopener noreferrer"&gt;Amazon Connect Customer Profiles&lt;/a&gt; para dar a los agentes una vista unificada del cliente a través de canales. Luego en tu Contact Flow, usa el &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles-block.html" rel="noopener noreferrer"&gt;bloque Customer Profiles&lt;/a&gt; para recuperar el perfil y mostrarlo en el workspace del agente. El agente ve el nombre del cliente, su handle de X y cualquier historial de interacciones previas — todo antes de escribir una respuesta.&lt;/p&gt;
&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Repositorio del Proyecto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Guía de Administrador de Amazon Connect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.x.com/en/docs" rel="noopener noreferrer"&gt;Documentación del X API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.x.com/en/docs/twitter-api/enterprise/account-activity-api/overview" rel="noopener noreferrer"&gt;X Account Activity API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.tweepy.org/" rel="noopener noreferrer"&gt;Documentación de Tweepy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/x_setup.md" rel="noopener noreferrer"&gt;Guía de Configuración de X&lt;/a&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>xchat</category>
      <category>amazonconnect</category>
      <category>cdk</category>
      <category>python</category>
    </item>
    <item>
      <title>Answer X DMs in Amazon Connect Chat</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Fri, 03 Apr 2026 17:06:29 +0000</pubDate>
      <link>https://dev.to/ensamblador/answer-x-dms-in-amazon-connect-chat-4705</link>
      <guid>https://dev.to/ensamblador/answer-x-dms-in-amazon-connect-chat-4705</guid>
      <description>&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;em&gt;Learn how to bridge X (Twitter) Direct Messages and Amazon Connect Chat for seamless customer service. This step-by-step guide covers the full architecture using AWS CDK, AWS Lambda, Amazon API Gateway, Amazon DynamoDB, and Amazon Connect. From receiving customer DMs to routing them to agents, forwarding agent replies back to X, and handling attachments in both directions — all with automatic session management, CRC webhook validation, and user profile caching via the Tweepy SDK.&lt;/em&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0on530id1p6mj2afzu9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0on530id1p6mj2afzu9.gif" alt="Demo" width="600" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your customers are already on X. They follow your brand, engage with your posts, and when they need help — they send a DM. If your support team has to switch between X and their contact center tool, you're losing time and context.&lt;/p&gt;

&lt;p&gt;In this blog, you'll learn how to connect X Direct Messages directly to Amazon Connect Chat, so your agents can handle X conversations from the same workspace they use for every other channel. No app switching, no copy-pasting, no lost messages.&lt;/p&gt;

&lt;p&gt;Check out the code at &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What you'll build
&lt;/h2&gt;

&lt;p&gt;A bidirectional messaging bridge between X DMs and Amazon Connect that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receives incoming X DMs via the Account Activity API webhook and routes them to Amazon Connect Chat&lt;/li&gt;
&lt;li&gt;Forwards agent replies from Amazon Connect back to X through the Tweepy SDK&lt;/li&gt;
&lt;li&gt;Manages chat sessions automatically — creating new ones, reusing active ones, and cleaning up expired ones&lt;/li&gt;
&lt;li&gt;Caches X user profiles (name, username, profile image) in DynamoDB to reduce API calls&lt;/li&gt;
&lt;li&gt;Handles attachments in both directions — images, videos, and GIFs from customers, and images and videos from agents&lt;/li&gt;
&lt;li&gt;Prevents echo loops by filtering out messages sent by your own X account&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The end result: agents see X conversations as regular chat contacts in their Amazon Connect workspace, complete with the customer's X display name and profile information.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcyklp9rw2ixwrah7cnrs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcyklp9rw2ixwrah7cnrs.gif" alt="Architecture Diagram" width="996" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how it flows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A customer sends a DM on X. The Account Activity API delivers the webhook event to an API Gateway endpoint&lt;/li&gt;
&lt;li&gt;The Inbound Handler Lambda validates the webhook (CRC challenge), parses the message, and looks up or creates an Amazon Connect Chat session&lt;/li&gt;
&lt;li&gt;The customer's X profile (if not present) is fetched via the Tweepy SDK and cached in DynamoDB&lt;/li&gt;
&lt;li&gt;Text messages and attachments are forwarded into the Connect Chat session via the Participant API&lt;/li&gt;
&lt;li&gt;When an agent replies, Amazon Connect publishes the event to an SNS topic via contact streaming&lt;/li&gt;
&lt;li&gt;The Outbound Handler Lambda picks up the SNS event, looks up the customer's X user ID, and sends the reply back as a DM through the Tweepy SDK&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Inbound: X → Amazon Connect
&lt;/h2&gt;

&lt;p&gt;When a customer sends a DM to your business X account, the inbound path handles everything from webhook validation to message delivery.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. CRC Webhook Validation
&lt;/h3&gt;

&lt;p&gt;X uses a Challenge-Response Check (CRC) to verify webhook ownership — this is fundamentally different from Meta's approach used in the Instagram and Facebook Messenger integrations. Instead of comparing a shared secret string, X sends a &lt;code&gt;crc_token&lt;/code&gt; that must be hashed with your Consumer Secret using HMAC-SHA256:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;compute_crc_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;crc_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;digest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;crc_token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;encoded_hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sha256=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;encoded_hash&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;X sends this challenge both during initial webhook registration and periodically afterward to re-validate. The Lambda handles it automatically on every GET request.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Message Parsing and Echo Prevention
&lt;/h3&gt;

&lt;p&gt;For POST requests (actual DM events), the &lt;code&gt;XService&lt;/code&gt; class parses the &lt;code&gt;direct_message_events&lt;/code&gt; payload. Each event contains the sender ID, recipient ID, text content, and any media attachments:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;XMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;message_create&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message_create&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;message_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message_data&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sender_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipient_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_create&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;target&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recipient_id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Parse attachment if present
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;media&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;media&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;media&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;media_url_https&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachment_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;media&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# photo, animated_gif, video
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The service filters out messages sent by your own X account ID to prevent echo loops — when your account sends a reply, X also delivers it as a webhook event.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. User Profile Retrieval and Caching
&lt;/h3&gt;

&lt;p&gt;X webhook payloads include inline user profile data in a &lt;code&gt;users&lt;/code&gt; dictionary, which the service extracts first. For any missing profiles, it falls back to a three-tier lookup:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Check in-memory cache first
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Check DynamoDB users table
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;users_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TableService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;db_profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;

    &lt;span class="c1"&gt;# Fetch from X API via Tweepy as last resort
&lt;/span&gt;    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;consumer_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;consumer_key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;consumer_secret&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;access_token_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;access_token_secret&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;profile_image_url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="c1"&gt;# ... cache in DynamoDB with 7-day TTL
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The profile includes &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, and &lt;code&gt;profile_image_url&lt;/code&gt;. Profiles are cached in a DynamoDB table with a 7-day TTL, so repeat conversations skip the API call entirely.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Session Management
&lt;/h3&gt;

&lt;p&gt;The handler checks DynamoDB for an existing chat session using the sender's X user ID:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a session exists, it sends the message using the stored &lt;code&gt;connectionToken&lt;/code&gt;. If the token is expired (AccessDeniedException), it automatically creates a new session.&lt;/li&gt;
&lt;li&gt;If no session exists, it calls &lt;code&gt;StartChatContact&lt;/code&gt; to create a new Amazon Connect Chat, starts contact streaming to the SNS topic, creates a participant connection, and stores everything in DynamoDB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The contact attributes include the channel name ("X"), the customer ID, and the customer's display name — making it easy to identify the source channel in Contact Flows and agent routing.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Attachment Handling (Inbound)
&lt;/h3&gt;

&lt;p&gt;When a customer sends an image, GIF, or video, the handler downloads it from X's CDN and uploads it to the Connect Chat session. X media URLs come in two flavors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pbs.twimg.com&lt;/code&gt; — publicly accessible, downloaded directly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ton.twitter.com&lt;/code&gt; — requires OAuth 1.0a authentication (using &lt;code&gt;requests_oauthlib&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The upload uses the three-step Participant API flow: &lt;code&gt;start_attachment_upload&lt;/code&gt; → PUT to pre-signed URL → &lt;code&gt;complete_attachment_upload&lt;/code&gt;. If anything fails, the handler falls back to sending the media URL as a text message.&lt;/p&gt;

&lt;p&gt;For attachments that include a caption, the text is cleaned up by stripping the auto-appended &lt;code&gt;t.co&lt;/code&gt; media link that X adds to the message body.&lt;/p&gt;
&lt;h2&gt;
  
  
  Outbound: Amazon Connect → X
&lt;/h2&gt;

&lt;p&gt;When an agent replies from the Amazon Connect workspace, the outbound path delivers the message back to X.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Streaming Events via SNS
&lt;/h3&gt;

&lt;p&gt;Amazon Connect publishes chat streaming events to an SNS topic. The Outbound Handler Lambda subscribes to this topic and processes three event types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MESSAGE&lt;/code&gt; — text messages from the agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ATTACHMENT&lt;/code&gt; — file attachments sent by the agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EVENT&lt;/code&gt; — participant join/leave and chat ended events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Messages from the &lt;code&gt;CUSTOMER&lt;/code&gt; role are skipped to avoid processing the customer's own messages again.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Sending Text Messages
&lt;/h3&gt;

&lt;p&gt;For text messages with &lt;code&gt;CUSTOMER&lt;/code&gt; or &lt;code&gt;ALL&lt;/code&gt; visibility, the handler looks up the customer's X user ID from DynamoDB and sends the reply via the Tweepy v2 API:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_x_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tweepy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;consumer_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;consumer_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;consumer_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;consumer_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;access_token_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_direct_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;participant_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Sending Attachments
&lt;/h3&gt;

&lt;p&gt;When an agent sends a file from the Connect Chat widget, the handler retrieves a signed URL for the attachment, downloads it, and uploads it to X via the v1.1 media upload endpoint (OAuth 1.0a). The media ID is then used to send a DM with the attachment:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MIME type&lt;/th&gt;
&lt;th&gt;X media category&lt;/th&gt;
&lt;th&gt;Upload method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;image/jpeg&lt;/code&gt;, &lt;code&gt;image/png&lt;/code&gt;, &lt;code&gt;image/webp&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dm_image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;media_upload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image/gif&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dm_gif&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chunked_upload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video/mp4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dm_video&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chunked_upload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;everything else&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Sent as plain-text link&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;X DMs only support images and videos as native media. Unsupported types (PDFs, documents, etc.) are sent as plain-text URLs so the customer still has access to the content.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Session Cleanup
&lt;/h3&gt;

&lt;p&gt;When a participant leaves or the chat ends, the handler deletes the connection record from DynamoDB so the next inbound message starts a fresh session.&lt;/p&gt;
&lt;h2&gt;
  
  
  Message Types Supported
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Text&lt;/th&gt;
&lt;th&gt;Images&lt;/th&gt;
&lt;th&gt;Videos&lt;/th&gt;
&lt;th&gt;GIFs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Inbound (customer → agent)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound (agent → customer)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Unsupported media types (PDFs, documents, etc.) are sent as plain-text links in the outbound direction.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Gets Deployed
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/webhooks&lt;/code&gt; endpoint (GET &amp;amp; POST)&lt;/td&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;Receives X CRC challenges (GET) and inbound DM events (POST)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Processes X DM events and routes them to Amazon Connect Chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Sends agent replies back to X as DMs via the Tweepy SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Active Connections table&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Tracks open chat sessions (&lt;code&gt;contactId&lt;/code&gt; PK, &lt;code&gt;userId&lt;/code&gt; GSI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X Users table&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Caches X user profiles (TTL-based expiry, 7 days)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;messages_out&lt;/code&gt; topic&lt;/td&gt;
&lt;td&gt;SNS&lt;/td&gt;
&lt;td&gt;Delivers Amazon Connect streaming events to the Outbound Handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x-dm-credentials&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;Stores X API OAuth 1.0a credentials (Consumer Key, Consumer Secret, Access Token, Access Token Secret)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/x/dm/config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Holds Connect instance ID, contact flow ID, and X account ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/x/dm/webhook/url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Stores the deployed API Gateway callback URL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Cost Estimation
&lt;/h2&gt;

&lt;p&gt;Example scenario: 1,000 conversations per month, averaging 10 messages each (5 inbound + 5 outbound), totaling 10,000 messages.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Estimated Monthly Cost&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure (API GW, Lambda, DynamoDB, SNS, Secrets Manager)&lt;/td&gt;
&lt;td&gt;~$0.71&lt;/td&gt;
&lt;td&gt;Negligible at this scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Inbound)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Outbound)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X API — Outbound DMs&lt;/td&gt;
&lt;td&gt;~$50.00&lt;/td&gt;
&lt;td&gt;5,000 DM sends × ~$0.01/request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$90.71&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;X API uses credit-based pay-per-use pricing. The per-endpoint cost shown above is approximate — actual rates are displayed in the &lt;a href="https://console.x.com/" rel="noopener noreferrer"&gt;X Developer Console&lt;/a&gt; and may change. See &lt;a href="https://aws.amazon.com/connect/pricing/" rel="noopener noreferrer"&gt;Amazon Connect pricing&lt;/a&gt; and &lt;a href="https://developer.x.com/en/products/twitter-api" rel="noopener noreferrer"&gt;X API pricing&lt;/a&gt; for current rates.&lt;/p&gt;

&lt;p&gt;To reduce Connect Chat costs on high-volume conversations, consider adding a &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/whatsapp-eum-connect-chat" rel="noopener noreferrer"&gt;message buffering layer&lt;/a&gt; to aggregate rapid consecutive messages.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deployment Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before getting started you'll need:&lt;/p&gt;
&lt;h3&gt;
  
  
  X Developer Account and API Credentials
&lt;/h3&gt;

&lt;p&gt;You need an X Developer Account with at least the Pay-Per-Use tier, and four OAuth 1.0a credentials (Consumer Key, Consumer Secret, Access Token, Access Token Secret).&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/x_setup.md" rel="noopener noreferrer"&gt;X Platform Setup Guide&lt;/a&gt; for detailed step-by-step instructions on creating your app, configuring permissions, and generating credentials.&lt;/p&gt;

&lt;p&gt;⚠️ Important: The free tier does not include webhook-based DM delivery. You need the Pay-Per-Use tier.&lt;/p&gt;
&lt;h3&gt;
  
  
  An Amazon Connect Instance
&lt;/h3&gt;

&lt;p&gt;You need an Amazon Connect instance. If you don't have one yet, you can &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;follow this guide&lt;/a&gt; to create one.&lt;/p&gt;

&lt;p&gt;You'll need the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; of your instance. You can find it in the Amazon Connect console or in the instance ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  A Chat Flow to Handle Messages
&lt;/h3&gt;

&lt;p&gt;Create or have ready the contact flow that defines the user experience. &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/create-contact-flow.html" rel="noopener noreferrer"&gt;Follow this guide&lt;/a&gt; to create an Inbound Contact Flow. The simplest one will work.&lt;/p&gt;

&lt;p&gt;Remember to publish the flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" alt="Simple Flow" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take note of the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; and &lt;strong&gt;CONTACT_FLOW_ID&lt;/strong&gt; from the Details tab. The values are in the flow ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID/contact-flow/CONTACT_FLOW_ID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(see the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_connect.md" rel="noopener noreferrer"&gt;Amazon Connect Prerequisites&lt;/a&gt; for more details)&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying with AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Deploy in the same region where your Amazon Connect instance is configured.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Clone the repository and navigate to the project
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-amazon-connect-social-integration.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-amazon-connect-social-integration/x-dm-connect-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Deploy with CDK
&lt;/h3&gt;

&lt;p&gt;Follow the instructions in the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;CDK Deployment Guide&lt;/a&gt; for environment setup and deployment commands.&lt;/p&gt;
&lt;h2&gt;
  
  
  Post-deployment Configuration
&lt;/h2&gt;

&lt;p&gt;After deployment, three configuration steps are needed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Update X API Credentials&lt;/strong&gt; — The stack creates a Secrets Manager secret named &lt;code&gt;x-dm-credentials&lt;/code&gt; with placeholder values. Update it with your actual Consumer Key, Consumer Secret, Access Token, and Access Token Secret.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Update SSM Configuration&lt;/strong&gt; — Update the SSM parameter &lt;code&gt;/x/dm/config&lt;/code&gt; with your Amazon Connect &lt;code&gt;instance_id&lt;/code&gt;, &lt;code&gt;contact_flow_id&lt;/code&gt;, and your X account's numeric &lt;code&gt;x_account_id&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Register the Webhook and Subscribe&lt;/strong&gt; — Register your deployed API Gateway URL with the X Account Activity API and subscribe your business account to receive DM events. The Inbound Handler responds to CRC challenges automatically.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For detailed instructions on each step, including how to find your &lt;code&gt;x_account_id&lt;/code&gt; and register the webhook, see the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/tree/main/x-dm-connect-chat" rel="noopener noreferrer"&gt;project README&lt;/a&gt; and the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/x_setup.md" rel="noopener noreferrer"&gt;X Platform Setup Guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Go to your Amazon Connect instance and &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/launch-ccp.html" rel="noopener noreferrer"&gt;open the Contact Control Panel (CCP)&lt;/a&gt;.&lt;/p&gt;





&lt;p&gt;Try these scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send a DM to your business X account from another X account — it should appear as a new chat contact in the CCP&lt;/li&gt;
&lt;li&gt;Reply from the CCP — the response should arrive in the customer's X DMs&lt;/li&gt;
&lt;li&gt;Send an image from X — it should appear as an image attachment in the agent's chat&lt;/li&gt;
&lt;li&gt;From the agent side, send an image — it should appear in the customer's X DMs&lt;/li&gt;
&lt;li&gt;Try sending a document from the agent side — it should arrive as a link in the customer's DMs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Important Considerations around X
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Encrypted DMs
&lt;/h3&gt;

&lt;p&gt;X supports end-to-end encrypted (E2EE) Direct Messages. However, &lt;strong&gt;encrypted DMs are not accessible via the X API&lt;/strong&gt;. This integration only processes standard (non-encrypted) DMs. If a conversation is encrypted, the webhook will not receive those message events.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pay-Per-Use Tier
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Pay-Per-Use&lt;/strong&gt; tier is required for Account Activity API access. The free tier does not include webhook-based DM delivery.&lt;/li&gt;
&lt;li&gt;Review &lt;a href="https://developer.x.com/en/products/twitter-api" rel="noopener noreferrer"&gt;X API pricing&lt;/a&gt; for current tier details and costs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  CRC Re-validation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;X periodically re-sends CRC challenges to verify your webhook is still valid. The Inbound Handler handles this automatically, but ensure the Secrets Manager credentials remain valid and the Lambda function stays deployed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  OAuth 1.0a Credentials
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All four credentials (Consumer Key, Consumer Secret, Access Token, Access Token Secret) must remain valid. If you regenerate any credential in the X Developer Portal, update the Secrets Manager secret immediately.&lt;/li&gt;
&lt;li&gt;The Access Token and Access Token Secret are tied to the specific X user account that owns the app. Ensure this is the business account that should receive and send DMs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Rate Limits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The X API enforces rate limits on DM endpoints. The Account Activity API has its own limits on webhook registrations and CRC validations.&lt;/li&gt;
&lt;li&gt;Monitor your usage in the &lt;a href="https://developer.x.com/" rel="noopener noreferrer"&gt;X Developer Portal&lt;/a&gt; dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;This solution handles the core X DM-to-Connect messaging flow. Some ideas to extend it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Amazon Bedrock to analyze inbound images and provide agents with context before they respond&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/agentic-self-service.html" rel="noopener noreferrer"&gt;Amazon Connect AI Agents&lt;/a&gt; for agentic self-service, letting customers resolve common issues without waiting for a human agent&lt;/li&gt;
&lt;li&gt;Combine with the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/tree/main/instagram-dm-connect-chat" rel="noopener noreferrer"&gt;Instagram DM integration&lt;/a&gt; and &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/tree/main/facebook-messenger-connect-chat" rel="noopener noreferrer"&gt;Facebook Messenger integration&lt;/a&gt; to handle all social channels from a single Amazon Connect instance&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Leverage Amazon Connect Customer Profiles
&lt;/h3&gt;

&lt;p&gt;This solution already fetches X profile data (name, username, profile image) and passes it as contact attributes. You can take this further by integrating with &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles.html" rel="noopener noreferrer"&gt;Amazon Connect Customer Profiles&lt;/a&gt; to give agents a unified view of the customer across channels. Then in your Contact Flow, use the &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles-block.html" rel="noopener noreferrer"&gt;Customer Profiles block&lt;/a&gt; to retrieve the profile and display it in the agent workspace. The agent sees the customer's name, X handle, and any previous interaction history — all before they even type a reply.&lt;/p&gt;
&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Project Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Amazon Connect Administrator Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.x.com/en/docs" rel="noopener noreferrer"&gt;X API Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.x.com/en/docs/twitter-api/enterprise/account-activity-api/overview" rel="noopener noreferrer"&gt;X Account Activity API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.tweepy.org/" rel="noopener noreferrer"&gt;Tweepy Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/x_setup.md" rel="noopener noreferrer"&gt;X Setup Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>xchat</category>
      <category>amazonconnect</category>
      <category>cdk</category>
      <category>python</category>
    </item>
    <item>
      <title>Facebook Messenger to Amazon Connect Chat</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Wed, 01 Apr 2026 19:00:00 +0000</pubDate>
      <link>https://dev.to/ensamblador/facebook-messenger-to-amazon-connect-chat-5ccg</link>
      <guid>https://dev.to/ensamblador/facebook-messenger-to-amazon-connect-chat-5ccg</guid>
      <description>&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;em&gt;Learn how to bridge Facebook Messenger and Amazon Connect Chat for seamless customer service. This step-by-step guide covers the full architecture using AWS CDK, AWS Lambda, Amazon API Gateway, Amazon DynamoDB, and Amazon Connect. From receiving customer messages to routing them to agents, forwarding agent replies back to Messenger, and handling attachments in both directions — all with automatic session management and user profile caching via the Graph API.&lt;/em&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznayx64j4novaixpetjg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznayx64j4novaixpetjg.gif" alt="Demo" width="674" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Facebook Messenger has over a billion active users. Many of them are already messaging your Facebook Page with questions about products, order status, or support requests. If your agents have to juggle between the Meta Business Suite and their contact center, context gets lost and response times suffer.&lt;/p&gt;

&lt;p&gt;In this blog, you'll learn how to connect Facebook Messenger directly to Amazon Connect Chat, so your agents handle Messenger conversations from the same workspace they use for every other channel. Messages flow in both directions — including images, documents, and files — with automatic session management and user profile enrichment.&lt;/p&gt;

&lt;p&gt;Check out the code at &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What you'll build
&lt;/h2&gt;

&lt;p&gt;A bidirectional messaging bridge between Facebook Messenger and Amazon Connect that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receives incoming Messenger messages via Meta webhooks and routes them to Amazon Connect Chat&lt;/li&gt;
&lt;li&gt;Forwards agent replies from Amazon Connect back to Messenger through the Send API&lt;/li&gt;
&lt;li&gt;Manages chat sessions automatically — creating new ones, reusing active ones, and cleaning up expired ones&lt;/li&gt;
&lt;li&gt;Fetches and caches Messenger user profiles (first name, last name, profile picture) via the Graph API&lt;/li&gt;
&lt;li&gt;Handles attachments in both directions — images and files from customers, and images and files from agents&lt;/li&gt;
&lt;li&gt;Prevents echo loops by filtering out messages sent by your own Page&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The end result: agents see Messenger conversations as regular chat contacts in their Amazon Connect workspace, with the customer's real name displayed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7mt81rujerqac2nyu8i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7mt81rujerqac2nyu8i.gif" alt="Architecture Diagram" width="1015" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how it flows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A customer sends a message on Facebook Messenger. Meta delivers the webhook event to an API Gateway endpoint&lt;/li&gt;
&lt;li&gt;The Inbound Handler Lambda validates the webhook, parses the message, and looks up or creates an Amazon Connect Chat session&lt;/li&gt;
&lt;li&gt;The customer's Messenger profile is fetched via the Graph API and cached in DynamoDB&lt;/li&gt;
&lt;li&gt;Text messages and attachments are forwarded into the Connect Chat session via the Participant API&lt;/li&gt;
&lt;li&gt;When an agent replies, Amazon Connect publishes the event to an SNS topic via contact streaming&lt;/li&gt;
&lt;li&gt;The Outbound Handler Lambda picks up the SNS event, looks up the customer's Page-Scoped ID (PSID), and sends the reply back through the Messenger Send API&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Inbound: Messenger → Amazon Connect
&lt;/h2&gt;

&lt;p&gt;When a customer sends a message to your Facebook Page, the inbound path handles everything from webhook validation to message delivery.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Webhook Validation and Message Parsing
&lt;/h3&gt;

&lt;p&gt;Meta sends webhook events to your API Gateway &lt;code&gt;/messages&lt;/code&gt; endpoint. The Lambda handles GET requests for webhook verification — Meta sends &lt;code&gt;hub.mode&lt;/code&gt;, &lt;code&gt;hub.verify_token&lt;/code&gt;, and &lt;code&gt;hub.challenge&lt;/code&gt; parameters that must be validated and echoed back.&lt;/p&gt;

&lt;p&gt;For POST requests, the &lt;code&gt;MessengerService&lt;/code&gt; class parses the webhook payload. Messenger webhooks arrive with &lt;code&gt;object: "page"&lt;/code&gt; and contain entries with messaging data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MessengerMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sender&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipient_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recipient&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;message_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachments&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The service filters out messages sent by your own Page ID to prevent echo loops — when your Page sends a reply, Meta also delivers it as a webhook event, and without this filter you'd get an infinite cycle.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. User Profile Retrieval and Caching
&lt;/h3&gt;

&lt;p&gt;Before routing the message to Connect, the handler fetches the sender's Messenger profile using the Graph API. Unlike Instagram (which returns a single &lt;code&gt;name&lt;/code&gt; field), Messenger provides &lt;code&gt;first_name&lt;/code&gt; and &lt;code&gt;last_name&lt;/code&gt; separately:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Check in-memory cache first
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;psid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;psid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Check DynamoDB users table
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;users_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TableService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;db_profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;psid&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;

    &lt;span class="c1"&gt;# Fetch from Graph API
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;profile_pic&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fields&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.facebook.com/v24.0/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;psid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# ... fetch and cache in DynamoDB with 7-day TTL
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The display name is built by concatenating &lt;code&gt;first_name&lt;/code&gt; and &lt;code&gt;last_name&lt;/code&gt;, and this is what the agent sees in the Connect Chat widget.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Session Management
&lt;/h3&gt;

&lt;p&gt;The handler checks DynamoDB for an existing chat session using the sender's PSID (Page-Scoped ID):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a session exists, it sends the message using the stored &lt;code&gt;connectionToken&lt;/code&gt;. If the token is expired (AccessDeniedException), it automatically creates a new session and cleans up the old record.&lt;/li&gt;
&lt;li&gt;If no session exists, it calls &lt;code&gt;StartChatContact&lt;/code&gt; to create a new Amazon Connect Chat, starts contact streaming to the SNS topic, creates a participant connection, and stores everything in DynamoDB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The contact attributes include the channel name ("Messenger"), the customer ID (PSID), and the customer's display name — making it easy to identify the source channel in Contact Flows and agent routing.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Channel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Messenger&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customerName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;start_chat_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_chat_contact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;InstanceId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ContactFlowId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contact_flow_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Attributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ParticipantDetails&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DisplayName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;InitialMessage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ContentType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/plain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;ChatDurationInMinutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat_duration_minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  4. Attachment Handling (Inbound)
&lt;/h3&gt;

&lt;p&gt;When a customer sends an image, video, audio, or file on Messenger, the attachment data includes a &lt;code&gt;type&lt;/code&gt; and a &lt;code&gt;payload.url&lt;/code&gt; pointing to Meta's CDN. The handler downloads the file content and uploads it to the Connect Chat session:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attachment_message_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connect_chat_service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender_profile&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Ensure a chat session exists (create one if needed)
&lt;/span&gt;    &lt;span class="c1"&gt;# ...
&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;att_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;payload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Download from Messenger CDN
&lt;/span&gt;        &lt;span class="n"&gt;file_bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;download_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;att_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Upload to Connect Chat via Participant API
&lt;/span&gt;        &lt;span class="n"&gt;attachment_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connect_chat_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;fileContents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;file_bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_attachment_filename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;fileType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ConnectionToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;connection_token&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The upload uses the same three-step Participant API flow: &lt;code&gt;start_attachment_upload&lt;/code&gt; → PUT to pre-signed URL → &lt;code&gt;complete_attachment_upload&lt;/code&gt;. If anything fails, the handler falls back to sending the CDN URL as a text message.&lt;/p&gt;
&lt;h2&gt;
  
  
  Outbound: Amazon Connect → Messenger
&lt;/h2&gt;

&lt;p&gt;When an agent replies from the Amazon Connect workspace, the outbound path delivers the message back to Messenger.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Streaming Events via SNS
&lt;/h3&gt;

&lt;p&gt;Amazon Connect publishes chat streaming events to an SNS topic. The Outbound Handler Lambda subscribes to this topic and processes three event types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MESSAGE&lt;/code&gt; — text messages from the agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ATTACHMENT&lt;/code&gt; — file attachments sent by the agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EVENT&lt;/code&gt; — participant join/leave and chat ended events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Messages from the &lt;code&gt;CUSTOMER&lt;/code&gt; role are skipped to avoid processing the customer's own messages again.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Sending Text Messages
&lt;/h3&gt;

&lt;p&gt;For text messages with &lt;code&gt;CUSTOMER&lt;/code&gt; or &lt;code&gt;ALL&lt;/code&gt; visibility, the handler looks up the customer's PSID from DynamoDB and sends the reply via the Messenger Send API:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_messenger_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.facebook.com/v24.0/me/messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recipient&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text_message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;url_with_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;?access_token=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# POST to Messenger Send API
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Sending Attachments
&lt;/h3&gt;

&lt;p&gt;When an agent sends a file from the Connect Chat widget, the handler retrieves a signed URL for the attachment and forwards it to Messenger as a media message. The MIME type determines the Messenger attachment type:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MIME prefix&lt;/th&gt;
&lt;th&gt;Messenger type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;audio/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audio&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;everything else&lt;/td&gt;
&lt;td&gt;&lt;code&gt;file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_messenger_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attachment_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;attachment_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_attachment_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# image, video, audio, or file
&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recipient&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attachment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;attachment_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;attachment_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_reusable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# POST to Messenger Send API
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;is_reusable: True&lt;/code&gt; flag tells Meta to cache the attachment, which can speed up delivery if the same file is sent to multiple recipients.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Session Cleanup
&lt;/h3&gt;

&lt;p&gt;When a participant leaves or the chat ends, the handler deletes the connection record from DynamoDB so the next inbound message starts a fresh session.&lt;/p&gt;
&lt;h2&gt;
  
  
  Message Types Supported
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Text&lt;/th&gt;
&lt;th&gt;Images&lt;/th&gt;
&lt;th&gt;Videos&lt;/th&gt;
&lt;th&gt;Audio&lt;/th&gt;
&lt;th&gt;Files&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Inbound (customer → agent)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound (agent → customer)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  What Gets Deployed
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/messages&lt;/code&gt; endpoint (GET &amp;amp; POST)&lt;/td&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;Receives Meta webhook verification and inbound messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Processes Messenger messages and routes them to Amazon Connect Chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Sends agent replies back to Messenger via the Send API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Active Connections table&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Tracks open chat sessions (&lt;code&gt;contactId&lt;/code&gt; PK, &lt;code&gt;userId&lt;/code&gt; GSI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Messenger Users table&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Caches Messenger user profiles (TTL-based expiry)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;messages_out&lt;/code&gt; topic&lt;/td&gt;
&lt;td&gt;SNS&lt;/td&gt;
&lt;td&gt;Delivers Amazon Connect streaming events to the Outbound Handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;messenger-page-token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;Stores the Facebook Page Access Token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/meta/messenger/config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Holds Connect instance ID, contact flow ID, verification token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/meta/messenger/webhook/url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Stores the deployed API Gateway callback URL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Cost Estimation
&lt;/h2&gt;

&lt;p&gt;Example scenario: 1,000 conversations per month, averaging 10 messages each (5 inbound + 5 outbound), totaling 10,000 messages.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Estimated Monthly Cost&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure (API GW, Lambda, DynamoDB, SNS, Secrets Manager)&lt;/td&gt;
&lt;td&gt;~$0.71&lt;/td&gt;
&lt;td&gt;Negligible at this scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Inbound)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Outbound)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$40.71&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The infrastructure cost is minimal — Amazon Connect Chat messaging is the primary cost driver at $0.004 per message in each direction. See &lt;a href="https://aws.amazon.com/connect/pricing/" rel="noopener noreferrer"&gt;Amazon Connect pricing&lt;/a&gt; for current rates.&lt;/p&gt;

&lt;p&gt;To reduce Connect Chat costs on high-volume conversations, consider adding a &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/whatsapp-eum-connect-chat" rel="noopener noreferrer"&gt;message buffering layer&lt;/a&gt; to aggregate rapid consecutive messages.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deployment Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before getting started you'll need:&lt;/p&gt;
&lt;h3&gt;
  
  
  Facebook Page and Meta App
&lt;/h3&gt;

&lt;p&gt;You need a Facebook Page and a Meta App configured with the Messenger Platform. The main steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have or create a Meta Business Account&lt;/li&gt;
&lt;li&gt;Create a Meta App and add the Messenger product&lt;/li&gt;
&lt;li&gt;Connect your Facebook Page and generate a Page Access Token&lt;/li&gt;
&lt;li&gt;Generate a non-expiring Page Access Token (short-lived tokens expire in ~1-2 hours)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/facebook_setup.md" rel="noopener noreferrer"&gt;Facebook Setup Guide&lt;/a&gt; for detailed step-by-step instructions, including the token exchange flow to get a non-expiring token.&lt;/p&gt;

&lt;p&gt;⚠️ Important: In development mode, your app can only receive messages from Facebook accounts with a role on the Meta App (Admin, Developer, Tester). For production use, you need App Review with Advanced Access for &lt;code&gt;pages_messaging&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  An Amazon Connect Instance
&lt;/h3&gt;

&lt;p&gt;You need an Amazon Connect instance. If you don't have one yet, you can &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;follow this guide&lt;/a&gt; to create one.&lt;/p&gt;

&lt;p&gt;You'll need the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; of your instance. You can find it in the Amazon Connect console or in the instance ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  A Chat Flow to Handle Messages
&lt;/h3&gt;

&lt;p&gt;Create or have ready the contact flow that defines the user experience. &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/create-contact-flow.html" rel="noopener noreferrer"&gt;Follow this guide&lt;/a&gt; to create an Inbound Contact Flow. The simplest one will work.&lt;/p&gt;

&lt;p&gt;Remember to publish the flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" alt="Simple Flow" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take note of the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; and &lt;strong&gt;CONTACT_FLOW_ID&lt;/strong&gt; from the Details tab. The values are in the flow ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID/contact-flow/CONTACT_FLOW_ID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(see the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_connect.md" rel="noopener noreferrer"&gt;Amazon Connect Prerequisites&lt;/a&gt; for more details)&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying with AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Deploy in the same region where your Amazon Connect instance is configured.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Clone the repository and navigate to the project
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-amazon-connect-social-integration.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-amazon-connect-social-integration/facebook-messenger-connect-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Deploy with CDK
&lt;/h3&gt;

&lt;p&gt;Follow the instructions in the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;CDK Deployment Guide&lt;/a&gt; for environment setup and deployment commands.&lt;/p&gt;
&lt;h2&gt;
  
  
  Post-deployment Configuration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Update the Page Access Token in Secrets Manager
&lt;/h3&gt;

&lt;p&gt;The stack creates a Secrets Manager secret named &lt;a href="https://console.aws.amazon.com/secretsmanager/secret?name=messenger-page-token" rel="noopener noreferrer"&gt;&lt;code&gt;messenger-page-token&lt;/code&gt;&lt;/a&gt; with a placeholder value. Update it with your actual non-expiring Page Access Token.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/facebook_setup.md#step-5-generate-a-long-lived-page-access-token" rel="noopener noreferrer"&gt;Facebook Setup Guide — Step 5&lt;/a&gt; for how to generate a non-expiring token.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Update the SSM Configuration Parameter
&lt;/h3&gt;

&lt;p&gt;After deployment, go to &lt;a href="https://console.aws.amazon.com/systems-manager/parameters" rel="noopener noreferrer"&gt;AWS Systems Manager - Parameter Store&lt;/a&gt; and update the SSM parameter &lt;code&gt;/meta/messenger/config&lt;/code&gt; with your Amazon Connect and Facebook details:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instance_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your Amazon Connect Instance ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contact_flow_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The ID of the Inbound Contact Flow for chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MESSENGER_VERIFICATION_TOKEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A secret string you choose — must match what you enter in the Meta webhook config&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Step 3: Configure the Webhook in Meta App Dashboard
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to your Meta App Dashboard → Messenger → Settings → Webhooks&lt;/li&gt;
&lt;li&gt;Set the &lt;strong&gt;Callback URL&lt;/strong&gt; to the API Gateway URL. You can find it in the SSM parameter &lt;code&gt;/meta/messenger/webhook/url&lt;/code&gt; in &lt;a href="https://console.aws.amazon.com/systems-manager/parameters" rel="noopener noreferrer"&gt;AWS Systems Manager - Parameter Store&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Set the &lt;strong&gt;Verify Token&lt;/strong&gt; to the same value you used for &lt;code&gt;MESSENGER_VERIFICATION_TOKEN&lt;/code&gt; above&lt;/li&gt;
&lt;li&gt;Subscribe to the &lt;code&gt;messages&lt;/code&gt; webhook field (at minimum)&lt;/li&gt;
&lt;li&gt;Subscribe your Page to the app so it receives webhook events&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For full details, see &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/facebook_setup.md#step-4-configure-webhooks" rel="noopener noreferrer"&gt;Facebook Setup Guide — Step 4&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Go to your Amazon Connect instance and &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/launch-ccp.html" rel="noopener noreferrer"&gt;open the Contact Control Panel (CCP)&lt;/a&gt;.&lt;/p&gt;





&lt;p&gt;Try these scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send a message to your Facebook Page from another Facebook account — it should appear as a new chat contact in the CCP&lt;/li&gt;
&lt;li&gt;Reply from the CCP — the response should arrive in the customer's Messenger chat&lt;/li&gt;
&lt;li&gt;Send an image from Messenger — it should appear as an image attachment in the agent's chat&lt;/li&gt;
&lt;li&gt;Send a file (PDF, document) from Messenger — it should appear as a file attachment&lt;/li&gt;
&lt;li&gt;From the agent side, send an image or document — it should appear in the customer's Messenger chat&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Important Considerations around Facebook Messenger
&lt;/h2&gt;
&lt;h3&gt;
  
  
  24-Hour Messaging Window
&lt;/h3&gt;

&lt;p&gt;Facebook Messenger has a &lt;strong&gt;24-hour standard messaging window&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After a user sends a message, your Page has 24 hours to respond&lt;/li&gt;
&lt;li&gt;Outside this window, you can only send messages using &lt;a href="https://developers.facebook.com/docs/messenger-platform/send-messages/message-tags" rel="noopener noreferrer"&gt;Message Tags&lt;/a&gt; (limited use cases)&lt;/li&gt;
&lt;li&gt;Each new user message reopens the 24-hour window&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Rate Limits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Messenger Platform has rate limits based on your app's usage tier&lt;/li&gt;
&lt;li&gt;Monitor API response headers for rate limit information&lt;/li&gt;
&lt;li&gt;Implement exponential backoff for retries&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  App Review
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;development mode&lt;/strong&gt;, your app can only receive messages from accounts with a role on the app (Admin, Developer, Tester)&lt;/li&gt;
&lt;li&gt;For production use with real customers, you need to submit for &lt;a href="https://developers.facebook.com/docs/app-review" rel="noopener noreferrer"&gt;App Review&lt;/a&gt; and request &lt;strong&gt;Advanced Access&lt;/strong&gt; for &lt;code&gt;pages_messaging&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Page Access Token Security
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The Page Access Token grants full messaging access to your Page — treat it like a password&lt;/li&gt;
&lt;li&gt;Store it in AWS Secrets Manager (as this solution does), never in code or environment variables&lt;/li&gt;
&lt;li&gt;Use the non-expiring token flow described in the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/facebook_setup.md" rel="noopener noreferrer"&gt;Facebook Setup Guide&lt;/a&gt; to avoid token rotation issues&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;This solution handles the core Messenger-to-Connect messaging flow. Some ideas to extend it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Amazon Bedrock to analyze inbound images and provide agents with context&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/agentic-self-service.html" rel="noopener noreferrer"&gt;Amazon Connect AI Agents&lt;/a&gt; for agentic self-service, letting customers resolve common issues without waiting for a human agent&lt;/li&gt;
&lt;li&gt;Combine with the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/tree/main/instagram-dm-connect-chat" rel="noopener noreferrer"&gt;Instagram DM integration&lt;/a&gt; to handle both Meta channels from a single Amazon Connect instance&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Leverage Amazon Connect Customer Profiles
&lt;/h3&gt;

&lt;p&gt;This solution already fetches Messenger profile data (first name, last name, profile picture) and passes it as contact attributes. You can take this further by integrating with &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles.html" rel="noopener noreferrer"&gt;Amazon Connect Customer Profiles&lt;/a&gt; to give agents a unified view of the customer across channels. Then in your Contact Flow, use the &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles-block.html" rel="noopener noreferrer"&gt;Customer Profiles block&lt;/a&gt; to retrieve the profile and display it in the agent workspace. The agent sees the customer's name, previous interaction history, and data from other channels — all before they even type a reply.&lt;/p&gt;
&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Project Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Amazon Connect Administrator Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/facebook_setup.md" rel="noopener noreferrer"&gt;Facebook Setup Guide&lt;/a&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>meta</category>
      <category>amazonconnect</category>
      <category>python</category>
      <category>cdk</category>
    </item>
    <item>
      <title>Expandiendo Amazon Connect a Facebook Messenger</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Wed, 01 Apr 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/ensamblador/expandiendo-connect-a-facebook-messenger-24ko</link>
      <guid>https://dev.to/ensamblador/expandiendo-connect-a-facebook-messenger-24ko</guid>
      <description>&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;em&gt;Aprende cómo conectar Facebook Messenger con Amazon Connect Chat para una atención al cliente fluida. Esta guía paso a paso cubre la arquitectura completa usando AWS CDK, AWS Lambda, Amazon API Gateway, Amazon DynamoDB y Amazon Connect. Desde recibir mensajes de clientes hasta enrutarlos a agentes, reenviar respuestas de agentes a Messenger y manejar archivos adjuntos en ambas direcciones — todo con gestión automática de sesiones, prevención de loops echo y caché de perfiles de usuario vía Graph API.&lt;/em&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznayx64j4novaixpetjg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznayx64j4novaixpetjg.gif" alt="Demo" width="674" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Facebook Messenger tiene más de mil millones de usuarios activos. Muchos de ellos ya están enviando mensajes a tu Página de Facebook con preguntas sobre productos, estado de pedidos o solicitudes de soporte. Si tus agentes tienen que alternar entre Meta Business Suite y su contact center, se pierde contexto y los tiempos de respuesta se resienten.&lt;/p&gt;

&lt;p&gt;En este blog, aprenderás cómo conectar Facebook Messenger directamente a Amazon Connect Chat, para que tus agentes manejen conversaciones de Messenger desde el mismo workspace que usan para todos los demás canales. Los mensajes fluyen en ambas direcciones — incluyendo imágenes, documentos y archivos — con gestión automática de sesiones y enriquecimiento de perfiles de usuario.&lt;/p&gt;

&lt;p&gt;Revisa el código en &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Qué vas a construir
&lt;/h2&gt;

&lt;p&gt;Un puente de mensajería bidireccional entre Facebook Messenger y Amazon Connect que:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Recibe mensajes entrantes de Messenger vía webhooks de Meta y los enruta a Amazon Connect Chat&lt;/li&gt;
&lt;li&gt;Reenvía las respuestas de los agentes desde Amazon Connect de vuelta a Messenger a través del Send API&lt;/li&gt;
&lt;li&gt;Gestiona sesiones de chat automáticamente — creando nuevas, reutilizando activas y limpiando las expiradas&lt;/li&gt;
&lt;li&gt;Obtiene y cachea perfiles de usuario de Messenger (nombre, apellido, foto de perfil) vía Graph API&lt;/li&gt;
&lt;li&gt;Maneja archivos adjuntos en ambas direcciones — imágenes y archivos de clientes, e imágenes y archivos de agentes&lt;/li&gt;
&lt;li&gt;Previene loops echo filtrando mensajes enviados por tu propia Página&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El resultado final: los agentes ven las conversaciones de Messenger como contactos de chat regulares en su workspace de Amazon Connect, con el nombre real del cliente.&lt;/p&gt;
&lt;h2&gt;
  
  
  Arquitectura
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7mt81rujerqac2nyu8i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7mt81rujerqac2nyu8i.gif" alt="Diagrama de Arquitectura" width="1015" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Así funciona el flujo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Un cliente envía un mensaje en Facebook Messenger. Meta entrega el evento webhook a un endpoint de API Gateway&lt;/li&gt;
&lt;li&gt;La Lambda del Inbound Handler valida el webhook, parsea el mensaje y busca o crea una sesión de Amazon Connect Chat&lt;/li&gt;
&lt;li&gt;El perfil de Messenger del cliente se obtiene vía Graph API y se cachea en DynamoDB&lt;/li&gt;
&lt;li&gt;Los mensajes de texto y archivos adjuntos se envían a la sesión de Connect Chat vía el Participant API&lt;/li&gt;
&lt;li&gt;Cuando un agente responde, Amazon Connect publica el evento a un topic SNS vía contact streaming&lt;/li&gt;
&lt;li&gt;La Lambda del Outbound Handler recibe el evento SNS, busca el Page-Scoped ID (PSID) del cliente y envía la respuesta a través del Messenger Send API&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Entrante: Messenger → Amazon Connect
&lt;/h2&gt;

&lt;p&gt;Cuando un cliente envía un mensaje a tu Página de Facebook, el flujo entrante maneja todo, desde la validación del webhook hasta la entrega del mensaje.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Validación del Webhook y Parseo de Mensajes
&lt;/h3&gt;

&lt;p&gt;Meta envía eventos webhook a tu endpoint &lt;code&gt;/messages&lt;/code&gt; de API Gateway. La Lambda maneja solicitudes GET para verificación del webhook — Meta envía los parámetros &lt;code&gt;hub.mode&lt;/code&gt;, &lt;code&gt;hub.verify_token&lt;/code&gt; y &lt;code&gt;hub.challenge&lt;/code&gt; que deben ser validados y devueltos.&lt;/p&gt;

&lt;p&gt;Para solicitudes POST, la clase &lt;code&gt;MessengerService&lt;/code&gt; parsea el payload del webhook. Los webhooks de Messenger llegan con &lt;code&gt;object: "page"&lt;/code&gt; y contienen entries con datos de mensajería:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MessengerMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sender&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipient_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recipient&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;message_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachments&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El servicio filtra mensajes enviados por tu propio Page ID para prevenir loops echo — cuando tu Página envía una respuesta, Meta también la entrega como evento webhook, y sin este filtro tendrías un ciclo infinito.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Obtención y Caché de Perfiles de Usuario
&lt;/h3&gt;

&lt;p&gt;Antes de enrutar el mensaje a Connect, el handler obtiene el perfil de Messenger del remitente usando el Graph API. A diferencia de Instagram (que retorna un solo campo &lt;code&gt;name&lt;/code&gt;), Messenger proporciona &lt;code&gt;first_name&lt;/code&gt; y &lt;code&gt;last_name&lt;/code&gt; por separado:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;psid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Verificar caché en memoria primero
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;psid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;psid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Verificar tabla de usuarios en DynamoDB
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;users_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TableService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;db_profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;psid&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;

    &lt;span class="c1"&gt;# Obtener del Graph API
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;profile_pic&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fields&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.facebook.com/v24.0/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;psid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# ... obtener y cachear en DynamoDB con TTL de 7 días
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El nombre para mostrar se construye concatenando &lt;code&gt;first_name&lt;/code&gt; y &lt;code&gt;last_name&lt;/code&gt;, y esto es lo que el agente ve en el widget de Connect Chat.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Gestión de Sesiones
&lt;/h3&gt;

&lt;p&gt;El handler verifica en DynamoDB si existe una sesión de chat activa usando el PSID (Page-Scoped ID) del remitente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Si existe una sesión, envía el mensaje usando el &lt;code&gt;connectionToken&lt;/code&gt; almacenado. Si el token expiró (AccessDeniedException), crea automáticamente una nueva sesión y limpia el registro anterior.&lt;/li&gt;
&lt;li&gt;Si no existe sesión, llama a &lt;code&gt;StartChatContact&lt;/code&gt; para crear un nuevo Amazon Connect Chat, inicia contact streaming al topic SNS, crea una conexión de participante y almacena todo en DynamoDB.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los atributos de contacto incluyen el nombre del canal ("Messenger"), el ID del cliente (PSID) y el nombre para mostrar del cliente — facilitando identificar el canal de origen en Contact Flows y enrutamiento de agentes.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Channel&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Messenger&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customerId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customerName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;start_chat_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_chat_contact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;InstanceId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;instance_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ContactFlowId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contact_flow_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Attributes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ParticipantDetails&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DisplayName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;InitialMessage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ContentType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/plain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;ChatDurationInMinutes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat_duration_minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  4. Manejo de Archivos Adjuntos (Entrante)
&lt;/h3&gt;

&lt;p&gt;Cuando un cliente envía una imagen, video, audio o archivo en Messenger, los datos del adjunto incluyen un &lt;code&gt;type&lt;/code&gt; y un &lt;code&gt;payload.url&lt;/code&gt; apuntando al CDN de Meta. El handler descarga el contenido del archivo y lo sube a la sesión de Connect Chat:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attachment_message_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connect_chat_service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;table_service&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sender_profile&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Asegurar que existe una sesión de chat (crear una si es necesario)
&lt;/span&gt;    &lt;span class="c1"&gt;# ...
&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;att_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;payload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Descargar del CDN de Messenger
&lt;/span&gt;        &lt;span class="n"&gt;file_bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;download_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;att_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Subir a Connect Chat vía Participant API
&lt;/span&gt;        &lt;span class="n"&gt;attachment_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connect_chat_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;fileContents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;file_bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;get_attachment_filename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;fileType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;content_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;ConnectionToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;connection_token&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;La carga usa el mismo flujo de tres pasos del Participant API: &lt;code&gt;start_attachment_upload&lt;/code&gt; → PUT a URL pre-firmada → &lt;code&gt;complete_attachment_upload&lt;/code&gt;. Si algo falla, el handler envía la URL del CDN como mensaje de texto como fallback.&lt;/p&gt;
&lt;h2&gt;
  
  
  Saliente: Amazon Connect → Messenger
&lt;/h2&gt;

&lt;p&gt;Cuando un agente responde desde el workspace de Amazon Connect, el flujo saliente entrega el mensaje de vuelta a Messenger.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Eventos de Streaming vía SNS
&lt;/h3&gt;

&lt;p&gt;Amazon Connect publica eventos de streaming de chat a un topic SNS. La Lambda del Outbound Handler se suscribe a este topic y procesa tres tipos de eventos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MESSAGE&lt;/code&gt; — mensajes de texto del agente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ATTACHMENT&lt;/code&gt; — archivos adjuntos enviados por el agente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EVENT&lt;/code&gt; — eventos de unión/salida de participantes y fin de chat&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los mensajes del rol &lt;code&gt;CUSTOMER&lt;/code&gt; se omiten para evitar procesar los propios mensajes del cliente nuevamente.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Envío de Mensajes de Texto
&lt;/h3&gt;

&lt;p&gt;Para mensajes de texto con visibilidad &lt;code&gt;CUSTOMER&lt;/code&gt; o &lt;code&gt;ALL&lt;/code&gt;, el handler busca el PSID del cliente en DynamoDB y envía la respuesta vía el Messenger Send API:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_messenger_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.facebook.com/v24.0/me/messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recipient&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text_message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;url_with_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;?access_token=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# POST al Messenger Send API
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Envío de Archivos Adjuntos
&lt;/h3&gt;

&lt;p&gt;Cuando un agente envía un archivo desde el widget de Connect Chat, el handler obtiene una URL firmada del adjunto y lo reenvía a Messenger como mensaje multimedia. El tipo MIME determina el tipo de adjunto de Messenger:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prefijo MIME&lt;/th&gt;
&lt;th&gt;Tipo Messenger&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;audio/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audio&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;todo lo demás&lt;/td&gt;
&lt;td&gt;&lt;code&gt;file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_messenger_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attachment_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;attachment_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_attachment_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# image, video, audio, o file
&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recipient&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attachment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;attachment_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;attachment_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;is_reusable&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# POST al Messenger Send API
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El flag &lt;code&gt;is_reusable: True&lt;/code&gt; le dice a Meta que cachee el adjunto, lo que puede acelerar la entrega si el mismo archivo se envía a múltiples destinatarios.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Limpieza de Sesiones
&lt;/h3&gt;

&lt;p&gt;Cuando un participante sale o el chat termina, el handler elimina el registro de conexión de DynamoDB para que el próximo mensaje entrante inicie una sesión nueva.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tipos de Mensajes Soportados
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dirección&lt;/th&gt;
&lt;th&gt;Texto&lt;/th&gt;
&lt;th&gt;Imágenes&lt;/th&gt;
&lt;th&gt;Videos&lt;/th&gt;
&lt;th&gt;Audio&lt;/th&gt;
&lt;th&gt;Archivos&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entrante (cliente → agente)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saliente (agente → cliente)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Qué se Despliega
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Recurso&lt;/th&gt;
&lt;th&gt;Servicio&lt;/th&gt;
&lt;th&gt;Propósito&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Endpoint &lt;code&gt;/messages&lt;/code&gt; (GET &amp;amp; POST)&lt;/td&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;Recibe verificación de webhook de Meta y mensajes entrantes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Procesa mensajes de Messenger y los enruta a Amazon Connect Chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Envía respuestas de agentes de vuelta a Messenger vía Send API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tabla Active Connections&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Rastrea sesiones de chat abiertas (&lt;code&gt;contactId&lt;/code&gt; PK, &lt;code&gt;userId&lt;/code&gt; GSI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tabla Messenger Users&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Cachea perfiles de usuario de Messenger (expiración por TTL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topic &lt;code&gt;messages_out&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;SNS&lt;/td&gt;
&lt;td&gt;Entrega eventos de streaming de Amazon Connect al Outbound Handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;messenger-page-token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;Almacena el Facebook Page Access Token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/meta/messenger/config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Contiene instance ID de Connect, contact flow ID, token de verificación&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/meta/messenger/webhook/url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Almacena la URL del callback de API Gateway desplegado&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Estimación de Costos
&lt;/h2&gt;

&lt;p&gt;Escenario de ejemplo: 1,000 conversaciones por mes, promediando 10 mensajes cada una (5 entrantes + 5 salientes), totalizando 10,000 mensajes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Componente&lt;/th&gt;
&lt;th&gt;Costo Mensual Estimado&lt;/th&gt;
&lt;th&gt;Notas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infraestructura (API GW, Lambda, DynamoDB, SNS, Secrets Manager)&lt;/td&gt;
&lt;td&gt;~$0.71&lt;/td&gt;
&lt;td&gt;Despreciable a esta escala&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Entrante)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Saliente)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$40.71&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;El costo de infraestructura es mínimo — la mensajería de Amazon Connect Chat es el principal generador de costos a $0.004 por mensaje en cada dirección. Consulta los &lt;a href="https://aws.amazon.com/connect/pricing/" rel="noopener noreferrer"&gt;precios de Amazon Connect&lt;/a&gt; para tarifas actuales.&lt;/p&gt;

&lt;p&gt;Para reducir costos de Connect Chat en conversaciones de alto volumen, considera agregar una &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/whatsapp-eum-connect-chat" rel="noopener noreferrer"&gt;capa de buffering de mensajes&lt;/a&gt; para agregar mensajes consecutivos rápidos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerrequisitos de Despliegue
&lt;/h2&gt;

&lt;p&gt;Antes de comenzar necesitarás:&lt;/p&gt;
&lt;h3&gt;
  
  
  Página de Facebook y Meta App
&lt;/h3&gt;

&lt;p&gt;Necesitas una Página de Facebook y una Meta App configurada con la Messenger Platform. Los pasos principales son:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tener o crear una Meta Business Account&lt;/li&gt;
&lt;li&gt;Crear una Meta App y agregar el producto Messenger&lt;/li&gt;
&lt;li&gt;Conectar tu Página de Facebook y generar un Page Access Token&lt;/li&gt;
&lt;li&gt;Generar un Page Access Token que no expire (los tokens de corta duración expiran en ~1-2 horas)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consulta la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/facebook_setup.md" rel="noopener noreferrer"&gt;Guía de Configuración de Facebook&lt;/a&gt; para instrucciones detalladas paso a paso, incluyendo el flujo de intercambio de tokens para obtener un token que no expire.&lt;/p&gt;

&lt;p&gt;⚠️ Importante: En modo desarrollo, tu app solo puede recibir mensajes de cuentas de Facebook con un rol en la Meta App (Admin, Developer, Tester). Para uso en producción, necesitas App Review con Advanced Access para &lt;code&gt;pages_messaging&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Una Instancia de Amazon Connect
&lt;/h3&gt;

&lt;p&gt;Necesitas una instancia de Amazon Connect. Si aún no tienes una, puedes &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;seguir esta guía&lt;/a&gt; para crear una.&lt;/p&gt;

&lt;p&gt;Necesitarás el &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; de tu instancia. Lo puedes encontrar en la consola de Amazon Connect o en el ARN de la instancia:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Un Flujo de Chat para Manejar Mensajes
&lt;/h3&gt;

&lt;p&gt;Crea o ten listo el flujo de contacto que define la experiencia del usuario. &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/create-contact-flow.html" rel="noopener noreferrer"&gt;Sigue esta guía&lt;/a&gt; para crear un Inbound Contact Flow. El más sencillo funcionará.&lt;/p&gt;

&lt;p&gt;Recuerda publicar el flujo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" alt="Flujo Simple" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Toma nota del &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; y &lt;strong&gt;CONTACT_FLOW_ID&lt;/strong&gt; en la pestaña de Detalles. Los valores están en el ARN del flujo:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID/contact-flow/CONTACT_FLOW_ID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(consulta los &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_connect.md" rel="noopener noreferrer"&gt;Prerrequisitos de Amazon Connect&lt;/a&gt; para más detalles)&lt;/p&gt;
&lt;h2&gt;
  
  
  Despliegue con AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Despliega en la misma región donde tu instancia de Amazon Connect está configurada.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Clona el repositorio y navega al proyecto
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-amazon-connect-social-integration.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-amazon-connect-social-integration/facebook-messenger-connect-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Despliega con CDK
&lt;/h3&gt;

&lt;p&gt;Sigue las instrucciones en la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;Guía de Despliegue CDK&lt;/a&gt; para configuración del entorno y comandos de despliegue.&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuración Post-despliegue
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Paso 1: Actualizar el Page Access Token en Secrets Manager
&lt;/h3&gt;

&lt;p&gt;El stack crea un secreto en Secrets Manager llamado &lt;a href="https://console.aws.amazon.com/secretsmanager/secret?name=messenger-page-token" rel="noopener noreferrer"&gt;&lt;code&gt;messenger-page-token&lt;/code&gt;&lt;/a&gt; con un valor placeholder. Actualízalo con tu Page Access Token real que no expire.&lt;/p&gt;

&lt;p&gt;Consulta &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/facebook_setup.md#step-5-generate-a-long-lived-page-access-token" rel="noopener noreferrer"&gt;Guía de Configuración de Facebook — Paso 5&lt;/a&gt; para saber cómo generar un token que no expire.&lt;/p&gt;
&lt;h3&gt;
  
  
  Paso 2: Actualizar el Parámetro de Configuración SSM
&lt;/h3&gt;

&lt;p&gt;Después del despliegue, ve a &lt;a href="https://console.aws.amazon.com/systems-manager/parameters" rel="noopener noreferrer"&gt;AWS Systems Manager - Parameter Store&lt;/a&gt; y actualiza el parámetro SSM &lt;code&gt;/meta/messenger/config&lt;/code&gt; con los detalles de Amazon Connect y Facebook:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parámetro&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instance_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tu Amazon Connect Instance ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contact_flow_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;El ID del Inbound Contact Flow para chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MESSENGER_VERIFICATION_TOKEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Un string secreto que tú eliges — debe coincidir con lo que ingreses en la configuración del webhook de Meta&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Paso 3: Configurar el Webhook en el Meta App Dashboard
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Ve a tu Meta App Dashboard → Messenger → Settings → Webhooks&lt;/li&gt;
&lt;li&gt;Configura la &lt;strong&gt;Callback URL&lt;/strong&gt; con la URL de API Gateway. La puedes encontrar en el parámetro SSM &lt;code&gt;/meta/messenger/webhook/url&lt;/code&gt; en &lt;a href="https://console.aws.amazon.com/systems-manager/parameters" rel="noopener noreferrer"&gt;AWS Systems Manager - Parameter Store&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Configura el &lt;strong&gt;Verify Token&lt;/strong&gt; con el mismo valor que usaste para &lt;code&gt;MESSENGER_VERIFICATION_TOKEN&lt;/code&gt; arriba&lt;/li&gt;
&lt;li&gt;Suscríbete al campo de webhook &lt;code&gt;messages&lt;/code&gt; (como mínimo)&lt;/li&gt;
&lt;li&gt;Suscribe tu Página a la app para que reciba eventos webhook&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para más detalles, consulta &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/facebook_setup.md#step-4-configure-webhooks" rel="noopener noreferrer"&gt;Guía de Configuración de Facebook — Paso 4&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pruebas
&lt;/h2&gt;

&lt;p&gt;Ve a tu instancia de Amazon Connect y &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/launch-ccp.html" rel="noopener noreferrer"&gt;abre el Contact Control Panel (CCP)&lt;/a&gt;.&lt;/p&gt;





&lt;p&gt;Prueba estos escenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Envía un mensaje a tu Página de Facebook desde otra cuenta de Facebook — debería aparecer como un nuevo contacto de chat en el CCP&lt;/li&gt;
&lt;li&gt;Responde desde el CCP — la respuesta debería llegar al chat de Messenger del cliente&lt;/li&gt;
&lt;li&gt;Envía una imagen desde Messenger — debería aparecer como un adjunto de imagen en el chat del agente&lt;/li&gt;
&lt;li&gt;Envía un archivo (PDF, documento) desde Messenger — debería aparecer como un adjunto de archivo&lt;/li&gt;
&lt;li&gt;Desde el lado del agente, envía una imagen o documento — debería aparecer en el chat de Messenger del cliente&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Consideraciones Importantes sobre Facebook Messenger
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Ventana de Mensajería de 24 Horas
&lt;/h3&gt;

&lt;p&gt;Facebook Messenger tiene una &lt;strong&gt;ventana de mensajería estándar de 24 horas&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Después de que un usuario envía un mensaje, tu Página tiene 24 horas para responder&lt;/li&gt;
&lt;li&gt;Fuera de esta ventana, solo puedes enviar mensajes usando &lt;a href="https://developers.facebook.com/docs/messenger-platform/send-messages/message-tags" rel="noopener noreferrer"&gt;Message Tags&lt;/a&gt; (casos de uso limitados)&lt;/li&gt;
&lt;li&gt;Cada nuevo mensaje del usuario reabre la ventana de 24 horas&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Límites de Tasa
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Messenger Platform tiene límites de tasa basados en el nivel de uso de tu app&lt;/li&gt;
&lt;li&gt;Monitorea los headers de respuesta del API para información de límites de tasa&lt;/li&gt;
&lt;li&gt;Implementa backoff exponencial para reintentos&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  App Review
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;En &lt;strong&gt;modo desarrollo&lt;/strong&gt;, tu app solo puede recibir mensajes de cuentas con un rol en la app (Admin, Developer, Tester)&lt;/li&gt;
&lt;li&gt;Para uso en producción con clientes reales, necesitas enviar para &lt;a href="https://developers.facebook.com/docs/app-review" rel="noopener noreferrer"&gt;App Review&lt;/a&gt; y solicitar &lt;strong&gt;Advanced Access&lt;/strong&gt; para &lt;code&gt;pages_messaging&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Seguridad del Page Access Token
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;El Page Access Token otorga acceso completo de mensajería a tu Página — trátalo como una contraseña&lt;/li&gt;
&lt;li&gt;Almacénalo en AWS Secrets Manager (como hace esta solución), nunca en código o variables de entorno&lt;/li&gt;
&lt;li&gt;Usa el flujo de token que no expire descrito en la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/facebook_setup.md" rel="noopener noreferrer"&gt;Guía de Configuración de Facebook&lt;/a&gt; para evitar problemas de rotación de tokens&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Próximos Pasos
&lt;/h2&gt;

&lt;p&gt;Esta solución maneja el flujo principal de mensajería Messenger-a-Connect. Algunas ideas para extenderla:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usar Amazon Bedrock para analizar imágenes entrantes y dar contexto a los agentes&lt;/li&gt;
&lt;li&gt;Usar &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/agentic-self-service.html" rel="noopener noreferrer"&gt;Amazon Connect AI Agents&lt;/a&gt; para autoservicio agéntico, permitiendo a los clientes resolver problemas comunes sin esperar a un agente humano&lt;/li&gt;
&lt;li&gt;Combinar con la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/tree/main/instagram-dm-connect-chat" rel="noopener noreferrer"&gt;integración de Instagram DM&lt;/a&gt; para manejar ambos canales de Meta desde una sola instancia de Amazon Connect&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Aprovechar Amazon Connect Customer Profiles
&lt;/h3&gt;

&lt;p&gt;Esta solución ya obtiene datos del perfil de Messenger (nombre, apellido, foto de perfil) y los pasa como atributos de contacto. Puedes ir más allá integrando con &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles.html" rel="noopener noreferrer"&gt;Amazon Connect Customer Profiles&lt;/a&gt; para dar a los agentes una vista unificada del cliente a través de canales. Luego en tu Contact Flow, usa el &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles-block.html" rel="noopener noreferrer"&gt;bloque Customer Profiles&lt;/a&gt; para recuperar el perfil y mostrarlo en el workspace del agente. El agente ve el nombre del cliente, historial de interacciones previas y datos de otros canales — todo antes de escribir una respuesta.&lt;/p&gt;
&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Repositorio del Proyecto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Guía de Administrador de Amazon Connect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/facebook_setup.md" rel="noopener noreferrer"&gt;Guía de Configuración de Facebook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=150,height=150,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>meta</category>
      <category>amazonconnect</category>
      <category>python</category>
      <category>cdk</category>
    </item>
    <item>
      <title>Respondiendo DMs de Instagram con Amazon Connect Chat</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Wed, 01 Apr 2026 06:58:52 +0000</pubDate>
      <link>https://dev.to/ensamblador/respondiendo-dms-de-instagram-con-amazon-connect-chat-3kc</link>
      <guid>https://dev.to/ensamblador/respondiendo-dms-de-instagram-con-amazon-connect-chat-3kc</guid>
      <description>&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;em&gt;Aprende cómo conectar los Mensajes Directos de Instagram con Amazon Connect Chat para una atención al cliente fluida. Esta guía paso a paso cubre la arquitectura completa usando AWS CDK, AWS Lambda, Amazon API Gateway, Amazon DynamoDB y Amazon Connect. Desde recibir DMs de clientes hasta enrutarlos a agentes, reenviar respuestas de agentes a Instagram y manejar archivos adjuntos en ambas direcciones — todo con gestión automática de sesiones y caché de perfiles de usuario.&lt;/em&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskr7bkqzv9zv4aflfy6s.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskr7bkqzv9zv4aflfy6s.gif" alt="Demo" width="466" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tus clientes ya están en Instagram. Navegan tus productos, revisan tus stories, y cuando tienen una pregunta — envían un DM. Si tu equipo de soporte tiene que alternar entre Instagram y su herramienta de contact center, estás perdiendo tiempo y contexto.&lt;/p&gt;

&lt;p&gt;En este blog, aprenderás cómo responder los Mensajes Directos de Instagram directamente a Amazon Connect Chat, para que tus agentes manejen conversaciones de Instagram desde el mismo workspace que usan para todos los demás canales. Sin cambiar de app, sin copiar y pegar, sin mensajes perdidos.&lt;/p&gt;

&lt;p&gt;Revisa el código en &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Qué vas a construir
&lt;/h2&gt;

&lt;p&gt;Un puente de mensajería bidireccional entre DMs de Instagram y Amazon Connect que:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Recibe DMs entrantes de Instagram vía webhooks de Meta y los enruta a Amazon Connect Chat&lt;/li&gt;
&lt;li&gt;Reenvía las respuestas de los agentes desde Amazon Connect de vuelta a Instagram a través del Graph API&lt;/li&gt;
&lt;li&gt;Gestiona sesiones de chat automáticamente — creando nuevas, reutilizando activas y limpiando las expiradas&lt;/li&gt;
&lt;li&gt;Cachea perfiles de usuario de Instagram (nombre, username, foto de perfil, cantidad de seguidores) en DynamoDB para reducir llamadas al API&lt;/li&gt;
&lt;li&gt;Maneja archivos adjuntos en ambas direcciones — imágenes de clientes, e imágenes y documentos de agentes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El resultado final: los agentes ven las conversaciones de Instagram como contactos de chat regulares en su workspace de Amazon Connect, con el nombre y la información de perfil del cliente.&lt;/p&gt;
&lt;h2&gt;
  
  
  Arquitectura
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84oybjo91zbmsfdanbwt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84oybjo91zbmsfdanbwt.gif" alt="Diagrama de Arquitectura" width="1001" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Así funciona el flujo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Un cliente envía un DM en Instagram. Meta entrega el evento webhook a un endpoint de API Gateway&lt;/li&gt;
&lt;li&gt;La Lambda del Inbound Handler valida el webhook, parsea el mensaje y busca o crea una sesión de Amazon Connect Chat&lt;/li&gt;
&lt;li&gt;El perfil de Instagram del cliente se obtiene vía Graph API y se cachea en DynamoDB&lt;/li&gt;
&lt;li&gt;Los mensajes de texto y archivos adjuntos se envían a la sesión de Connect Chat vía el Participant API&lt;/li&gt;
&lt;li&gt;Cuando un agente responde, Amazon Connect publica el evento a un topic SNS vía contact streaming&lt;/li&gt;
&lt;li&gt;La Lambda del Outbound Handler recibe el evento SNS, busca el ID de Instagram del cliente y envía la respuesta a través del Instagram Graph API&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Entrante: Instagram → Amazon Connect
&lt;/h2&gt;

&lt;p&gt;Cuando un cliente envía un DM a tu cuenta Business de Instagram, el flujo entrante maneja todo, desde la validación del webhook hasta la entrega del mensaje.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Validación del Webhook y Parseo de Mensajes
&lt;/h3&gt;

&lt;p&gt;Meta envía eventos webhook a tu endpoint &lt;code&gt;/messages&lt;/code&gt; de API Gateway. La Lambda primero maneja las solicitudes GET para verificación del webhook — Meta envía un token de desafío que debe ser devuelto con el token de verificación correcto.&lt;/p&gt;

&lt;p&gt;Para solicitudes POST (mensajes reales), la clase &lt;code&gt;InstagramService&lt;/code&gt; parsea el payload del webhook. Los webhooks de Instagram llegan con &lt;code&gt;object: "instagram"&lt;/code&gt; y contienen entries con datos de mensajería:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstagramMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sender&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipient_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recipient&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;message_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachments&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El servicio también filtra mensajes echo — mensajes enviados por tu propia cuenta de Instagram — para prevenir loops infinitos.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Obtención y Caché de Perfiles de Usuario
&lt;/h3&gt;

&lt;p&gt;Antes de enrutar el mensaje a Connect, el handler obtiene el perfil de Instagram del remitente usando el Graph API. Esto le da al agente contexto sobre con quién está hablando:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instagram_scoped_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Verificar caché en memoria primero
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;instagram_scoped_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;instagram_scoped_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Verificar tabla de usuarios en DynamoDB
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;users_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TableService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;db_profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;instagram_scoped_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;

    &lt;span class="c1"&gt;# Obtener del Graph API como último recurso
&lt;/span&gt;    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fields&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.instagram.com/v24.0/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;instagram_scoped_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# ... obtener y cachear
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El perfil incluye campos como &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;profile_pic&lt;/code&gt;, &lt;code&gt;follower_count&lt;/code&gt;, &lt;code&gt;is_user_follow_business&lt;/code&gt; e &lt;code&gt;is_verified_user&lt;/code&gt;. Los perfiles se cachean en una tabla DynamoDB con un TTL de 7 días, así que las conversaciones repetidas se saltan la llamada al API.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Gestión de Sesiones
&lt;/h3&gt;

&lt;p&gt;El handler verifica en DynamoDB si existe una sesión de chat activa usando el Instagram-scoped ID del remitente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Si existe una sesión, envía el mensaje usando el &lt;code&gt;connectionToken&lt;/code&gt; almacenado. Si el token expiró (AccessDeniedException), crea automáticamente una nueva sesión.&lt;/li&gt;
&lt;li&gt;Si no existe sesión, llama a &lt;code&gt;StartChatContact&lt;/code&gt; para crear un nuevo Amazon Connect Chat, inicia contact streaming al topic SNS, crea una conexión de participante y almacena todo en DynamoDB.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  4. Manejo de Archivos Adjuntos (Entrante)
&lt;/h3&gt;

&lt;p&gt;Cuando un cliente envía una imagen, el handler la descarga del CDN de Instagram y la sube a la sesión de Connect Chat usando el flujo de tres pasos del Participant API:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;start_attachment_upload&lt;/code&gt; — crea un slot de carga con una URL pre-firmada&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUT&lt;/code&gt; a la URL pre-firmada — sube el contenido binario&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;complete_attachment_upload&lt;/code&gt; — finaliza la carga&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si la descarga o carga falla, el handler envía la URL del adjunto como mensaje de texto para que el agente aún tenga acceso al contenido.&lt;/p&gt;
&lt;h2&gt;
  
  
  Saliente: Amazon Connect → Instagram
&lt;/h2&gt;

&lt;p&gt;Cuando un agente responde desde el workspace de Amazon Connect, el flujo saliente entrega el mensaje de vuelta a Instagram.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Eventos de Streaming vía SNS
&lt;/h3&gt;

&lt;p&gt;Amazon Connect publica eventos de streaming de chat a un topic SNS. La Lambda del Outbound Handler se suscribe a este topic y procesa tres tipos de eventos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MESSAGE&lt;/code&gt; — mensajes de texto del agente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ATTACHMENT&lt;/code&gt; — archivos adjuntos enviados por el agente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EVENT&lt;/code&gt; — eventos de unión/salida de participantes y fin de chat&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los mensajes del rol &lt;code&gt;CUSTOMER&lt;/code&gt; se omiten para evitar loops echo.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Envío de Mensajes de Texto
&lt;/h3&gt;

&lt;p&gt;Para mensajes de texto con visibilidad &lt;code&gt;CUSTOMER&lt;/code&gt; o &lt;code&gt;ALL&lt;/code&gt;, el handler busca el Instagram-scoped ID del cliente y el ID de la cuenta Business de Instagram en DynamoDB, luego envía la respuesta vía el Graph API:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_instagram_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instagram_account_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.instagram.com/v24.0/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;instagram_account_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recipient&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text_message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# POST al Instagram Graph API
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Envío de Archivos Adjuntos
&lt;/h3&gt;

&lt;p&gt;Cuando un agente envía un archivo desde el widget de Connect Chat, el handler obtiene una URL firmada del adjunto y lo reenvía a Instagram como mensaje multimedia. El tipo MIME determina el tipo de mensaje de Instagram:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prefijo MIME&lt;/th&gt;
&lt;th&gt;Tipo Instagram&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;audio/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audio&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;todo lo demás&lt;/td&gt;
&lt;td&gt;&lt;code&gt;file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  4. Limpieza de Sesiones
&lt;/h3&gt;

&lt;p&gt;Cuando un participante sale o el chat termina, el handler elimina el registro de conexión de DynamoDB para que el próximo mensaje entrante inicie una sesión nueva.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tipos de Mensajes Soportados
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dirección&lt;/th&gt;
&lt;th&gt;Texto&lt;/th&gt;
&lt;th&gt;Imágenes&lt;/th&gt;
&lt;th&gt;Documentos&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entrante (cliente → agente)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saliente (agente → cliente)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Enviar documentos desde la app de Instagram no es posible actualmente (limitaciones de la app de Instagram), pero los clientes pueden recibir documentos enviados por agentes desde Amazon Connect.&lt;/p&gt;
&lt;h2&gt;
  
  
  Qué se Despliega
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Recurso&lt;/th&gt;
&lt;th&gt;Servicio&lt;/th&gt;
&lt;th&gt;Propósito&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Endpoint &lt;code&gt;/messages&lt;/code&gt; (GET &amp;amp; POST)&lt;/td&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;Recibe verificación de webhook de Meta y mensajes entrantes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Procesa mensajes de Instagram y los enruta a Amazon Connect Chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Envía respuestas de agentes de vuelta a Instagram vía Graph API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tabla Active Connections&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Rastrea sesiones de chat abiertas (&lt;code&gt;contactId&lt;/code&gt; PK, &lt;code&gt;userId&lt;/code&gt; GSI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tabla Instagram Users&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Cachea perfiles de usuario de Instagram (expiración por TTL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Topic &lt;code&gt;messages_out&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;SNS&lt;/td&gt;
&lt;td&gt;Entrega eventos de streaming de Amazon Connect al Outbound Handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instagram-token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;Almacena el Instagram User Access Token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/meta/instagram/config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Contiene instance ID de Connect, contact flow ID, token de verificación e Instagram account ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/meta/instagram/webhook/url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Almacena la URL del callback de API Gateway desplegado&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Estimación de Costos
&lt;/h2&gt;

&lt;p&gt;Escenario de ejemplo: 1,000 conversaciones por mes, promediando 10 mensajes cada una (5 entrantes + 5 salientes), totalizando 10,000 mensajes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Componente&lt;/th&gt;
&lt;th&gt;Costo Mensual Estimado&lt;/th&gt;
&lt;th&gt;Notas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infraestructura (API GW, Lambda, DynamoDB, SNS, Secrets Manager)&lt;/td&gt;
&lt;td&gt;~$0.71&lt;/td&gt;
&lt;td&gt;Despreciable a esta escala&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Entrante)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Saliente)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$40.71&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;El costo de infraestructura es mínimo — la mensajería de Amazon Connect Chat es el principal generador de costos a $0.004 por mensaje en cada dirección. Consulta los &lt;a href="https://aws.amazon.com/connect/pricing/" rel="noopener noreferrer"&gt;precios de Amazon Connect&lt;/a&gt; para tarifas actuales.&lt;/p&gt;

&lt;p&gt;Para reducir costos de Connect Chat en conversaciones de alto volumen, considera agregar una &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/whatsapp-eum-connect-chat" rel="noopener noreferrer"&gt;capa de buffering de mensajes&lt;/a&gt; para agregar mensajes consecutivos rápidos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerrequisitos de Despliegue
&lt;/h2&gt;

&lt;p&gt;Antes de comenzar necesitarás:&lt;/p&gt;
&lt;h3&gt;
  
  
  Cuenta Business de Instagram y Meta App
&lt;/h3&gt;

&lt;p&gt;Necesitas una cuenta de Instagram Business o Creator conectada a una Meta App con el API de Instagram configurado. Los pasos principales son:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tener o crear una Meta Business Account&lt;/li&gt;
&lt;li&gt;Crear una Meta App y agregar el producto de Instagram&lt;/li&gt;
&lt;li&gt;Configurar Instagram Login y generar un Instagram User Access Token&lt;/li&gt;
&lt;li&gt;Asegurarte de que tu cuenta de Instagram sea Business o Creator (las cuentas personales no soportan el Messaging API)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Consulta la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/instagram_setup.md" rel="noopener noreferrer"&gt;Guía de Configuración de Instagram&lt;/a&gt; para instrucciones detalladas paso a paso.&lt;/p&gt;

&lt;p&gt;⚠️ Importante: En modo desarrollo, tu app solo puede recibir mensajes de cuentas de Instagram con un rol en la Meta App.&lt;/p&gt;
&lt;h3&gt;
  
  
  Una Instancia de Amazon Connect
&lt;/h3&gt;

&lt;p&gt;Necesitas una instancia de Amazon Connect. Si aún no tienes una, puedes &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;seguir esta guía&lt;/a&gt; para crear una.&lt;/p&gt;

&lt;p&gt;Necesitarás el &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; de tu instancia. Lo puedes encontrar en la consola de Amazon Connect o en el ARN de la instancia:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Un Flujo de Chat para Manejar Mensajes
&lt;/h3&gt;

&lt;p&gt;Crea o ten listo el flujo de contacto que define la experiencia del usuario. &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/create-contact-flow.html" rel="noopener noreferrer"&gt;Sigue esta guía&lt;/a&gt; para crear un Inbound Contact Flow. El más sencillo funcionará.&lt;/p&gt;

&lt;p&gt;Recuerda publicar el flujo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" alt="Flujo Simple" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Toma nota del &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; y &lt;strong&gt;CONTACT_FLOW_ID&lt;/strong&gt; en la pestaña de Detalles. Los valores están en el ARN del flujo:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID/contact-flow/CONTACT_FLOW_ID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(consulta los &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_connect.md" rel="noopener noreferrer"&gt;Prerrequisitos de Amazon Connect&lt;/a&gt; para más detalles)&lt;/p&gt;
&lt;h2&gt;
  
  
  Despliegue con AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Despliega en la misma región donde tu instancia de Amazon Connect está configurada.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Clona el repositorio y navega al proyecto
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-amazon-connect-social-integration.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-amazon-connect-social-integration/instagram-dm-connect-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Despliega con CDK
&lt;/h3&gt;

&lt;p&gt;Sigue las instrucciones en la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;Guía de Despliegue CDK&lt;/a&gt; para configuración del entorno y comandos de despliegue.&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuración Post-despliegue
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Paso 1: Actualizar el Instagram Access Token en Secrets Manager
&lt;/h3&gt;

&lt;p&gt;El stack crea un secreto en Secrets Manager llamado &lt;a href="https://console.aws.amazon.com/secretsmanager/secret?name=instagram-token" rel="noopener noreferrer"&gt;&lt;code&gt;instagram-token&lt;/code&gt;&lt;/a&gt; con un valor placeholder. Actualízalo con tu Instagram User Access Token real.&lt;/p&gt;

&lt;p&gt;Consulta la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/instagram_setup.md" rel="noopener noreferrer"&gt;Guía de Configuración de Instagram&lt;/a&gt; para saber cómo generar este token.&lt;/p&gt;
&lt;h3&gt;
  
  
  Paso 2: Actualizar el Parámetro de Configuración SSM
&lt;/h3&gt;

&lt;p&gt;Después del despliegue, ve a &lt;a href="https://console.aws.amazon.com/systems-manager/parameters" rel="noopener noreferrer"&gt;AWS Systems Manager - Parameter Store&lt;/a&gt; y actualiza el parámetro SSM &lt;code&gt;/meta/instagram/config&lt;/code&gt; con los detalles de Amazon Connect e Instagram:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parámetro&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instance_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tu Amazon Connect Instance ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contact_flow_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;El ID del Inbound Contact Flow para chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INSTAGRAM_VERIFICATION_TOKEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Un string secreto que tú eliges — debe coincidir con lo que ingreses en la configuración del webhook de Meta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instagram_account_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tu Instagram Business Account ID (ver nota abajo)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Para encontrar tu &lt;code&gt;instagram_account_id&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;En tu Meta App Dashboard → Instagram → API Setup with Instagram Login → expande "1. Generate access tokens" → el ID está debajo de la cuenta de Instagram vinculada&lt;/li&gt;
&lt;li&gt;O llama al Graph API:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"https://graph.instagram.com/me?fields=id,username,account_type,user_id&amp;amp;access_token=YOUR_IG_ACCESS_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El campo &lt;code&gt;user_id&lt;/code&gt; en la respuesta es tu &lt;code&gt;instagram_account_id&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Paso 3: Configurar el Webhook en el Meta App Dashboard
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Ve a tu Meta App Dashboard → Instagram → API Setup with Instagram Login → Webhooks&lt;/li&gt;
&lt;li&gt;Configura la &lt;strong&gt;Callback URL&lt;/strong&gt; con la URL de API Gateway. La puedes encontrar en el parámetro SSM &lt;code&gt;/meta/instagram/webhook/url&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configura el &lt;strong&gt;Verify Token&lt;/strong&gt; con el mismo valor que usaste para &lt;code&gt;INSTAGRAM_VERIFICATION_TOKEN&lt;/code&gt; arriba&lt;/li&gt;
&lt;li&gt;Suscríbete al campo de webhook &lt;code&gt;messages&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Para más detalles, consulta la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/instagram_setup.md" rel="noopener noreferrer"&gt;Guía de Configuración de Instagram&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pruebas
&lt;/h2&gt;

&lt;p&gt;Ve a tu instancia de Amazon Connect y &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/launch-ccp.html" rel="noopener noreferrer"&gt;abre el Contact Control Panel (CCP)&lt;/a&gt;.&lt;/p&gt;





&lt;p&gt;Prueba estos escenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Envía un DM a tu cuenta Business de Instagram desde otra cuenta de Instagram — debería aparecer como un nuevo contacto de chat en el CCP&lt;/li&gt;
&lt;li&gt;Responde desde el CCP — la respuesta debería llegar a los DMs de Instagram del cliente&lt;/li&gt;
&lt;li&gt;Envía una imagen desde Instagram — debería aparecer como un adjunto de imagen en el chat del agente&lt;/li&gt;
&lt;li&gt;Desde el lado del agente, envía una imagen o documento — debería aparecer en los DMs de Instagram del cliente&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Consideraciones Importantes sobre Instagram
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Ventana de Mensajería de 24 Horas
&lt;/h3&gt;

&lt;p&gt;Instagram tiene una &lt;strong&gt;ventana de mensajería estándar de 24 horas&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Después de que un usuario envía un mensaje, tu cuenta tiene 24 horas para responder&lt;/li&gt;
&lt;li&gt;Fuera de esta ventana, la mensajería está restringida&lt;/li&gt;
&lt;li&gt;Cada nuevo mensaje del usuario reabre la ventana de 24 horas&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Ventana de Agente Humano
&lt;/h3&gt;

&lt;p&gt;Instagram proporciona una &lt;strong&gt;ventana de agente humano de 7 días&lt;/strong&gt; para conversaciones que son escaladas a un agente humano. Esta ventana extendida permite a los agentes más tiempo para resolver problemas complejos.&lt;/p&gt;
&lt;h3&gt;
  
  
  App Review
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;En &lt;strong&gt;modo desarrollo&lt;/strong&gt;, tu app solo puede recibir mensajes de cuentas de Instagram con un rol en la Meta App (Admin, Developer, Tester)&lt;/li&gt;
&lt;li&gt;Para uso en producción con clientes reales, necesitas enviar para &lt;a href="https://developers.facebook.com/docs/app-review" rel="noopener noreferrer"&gt;App Review&lt;/a&gt; y solicitar los permisos requeridos&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Límites de Tasa
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;El Instagram Messaging API tiene límites de tasa basados en el nivel de uso de tu app&lt;/li&gt;
&lt;li&gt;Monitorea los headers de respuesta del API para información de límites de tasa&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Próximos Pasos
&lt;/h2&gt;

&lt;p&gt;Esta solución maneja el flujo principal de mensajería Instagram-a-Connect. Algunas ideas para extenderla:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Agregar soporte para menciones y respuestas de Instagram Stories&lt;/li&gt;
&lt;li&gt;Usar Amazon Bedrock para analizar imágenes entrantes y dar contexto a los agentes antes de que respondan&lt;/li&gt;
&lt;li&gt;Usar &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/agentic-self-service.html" rel="noopener noreferrer"&gt;Amazon Connect AI Agents&lt;/a&gt; para autoservicio agéntico, permitiendo a los clientes resolver problemas comunes sin esperar a un agente humano&lt;/li&gt;
&lt;li&gt;Combinar con la &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/tree/main/facebook-messenger-connect-chat" rel="noopener noreferrer"&gt;integración de Facebook Messenger&lt;/a&gt; para manejar ambos canales de Meta desde una sola instancia de Amazon Connect&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Aprovechar Amazon Connect Customer Profiles
&lt;/h3&gt;

&lt;p&gt;Esta solución ya obtiene datos del perfil de Instagram (nombre, username, foto de perfil, cantidad de seguidores) y los pasa como atributos de contacto. Puedes ir más allá integrando con &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles.html" rel="noopener noreferrer"&gt;Amazon Connect Customer Profiles&lt;/a&gt; para dar a los agentes una vista unificada del cliente a través de canales. Luego en tu Contact Flow, usa el &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles-block.html" rel="noopener noreferrer"&gt;bloque Customer Profiles&lt;/a&gt; para recuperar el perfil y mostrarlo en el workspace del agente. El agente ve el nombre del cliente, su handle de Instagram, cantidad de seguidores y cualquier historial de interacciones previas — todo antes de escribir una respuesta.&lt;/p&gt;
&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Repositorio del Proyecto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Guía de Administrador de Amazon Connect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/messaging-api" rel="noopener noreferrer"&gt;Instagram Messaging API — Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/api-reference" rel="noopener noreferrer"&gt;Instagram Graph API — User Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/graph-api/webhooks/getting-started" rel="noopener noreferrer"&gt;Meta Webhooks — Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/instagram_setup.md" rel="noopener noreferrer"&gt;Guía de Configuración de Instagram&lt;/a&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>amazonconnect</category>
      <category>instagram</category>
      <category>python</category>
      <category>chat</category>
    </item>
    <item>
      <title>Instagram DMs to Amazon Connect Chat</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Wed, 01 Apr 2026 06:33:57 +0000</pubDate>
      <link>https://dev.to/ensamblador/instagram-dms-to-amazon-connect-chat-5e0l</link>
      <guid>https://dev.to/ensamblador/instagram-dms-to-amazon-connect-chat-5e0l</guid>
      <description>&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;em&gt;Learn how to bridge Instagram Direct Messages and Amazon Connect Chat for seamless customer service. This step-by-step guide covers the full architecture using AWS CDK, AWS Lambda, Amazon API Gateway, Amazon DynamoDB, and Amazon Connect. From receiving customer DMs to routing them to agents, forwarding agent replies back to Instagram, and handling attachments in both directions — all with automatic session management and user profile caching.&lt;/em&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskr7bkqzv9zv4aflfy6s.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskr7bkqzv9zv4aflfy6s.gif" alt="Demo" width="466" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your customers are already on Instagram. They browse your products, check your stories, and when they have a question — they send a DM. If your CX team has to switch between Instagram and their contact center, you're losing time and context.&lt;/p&gt;

&lt;p&gt;In this blog, you'll learn how to connect Instagram Direct Messages directly to Amazon Connect Chat, so your agents can handle Instagram conversations from the same workspace they use for every other channel. No app switching, no copy-pasting, no lost messages.&lt;/p&gt;

&lt;p&gt;Check out the code at &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What you'll build
&lt;/h2&gt;

&lt;p&gt;A bidirectional messaging bridge between Instagram DMs and Amazon Connect that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receives incoming Instagram DMs via Meta webhooks and routes them to Amazon Connect Chat&lt;/li&gt;
&lt;li&gt;Forwards agent replies from Amazon Connect back to Instagram through the Graph API&lt;/li&gt;
&lt;li&gt;Manages chat sessions automatically — creating new ones, reusing active ones, and cleaning up expired ones&lt;/li&gt;
&lt;li&gt;Caches Instagram user profiles (name, username, profile picture, follower count) in DynamoDB to reduce API calls&lt;/li&gt;
&lt;li&gt;Handles attachments in both directions — images from customers, and images and documents from agents&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The end result: agents see Instagram conversations as regular chat contacts in their Amazon Connect workspace, complete with the customer's Instagram name and profile information.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84oybjo91zbmsfdanbwt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F84oybjo91zbmsfdanbwt.gif" alt="Architecture Diagram" width="1001" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how it flows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A customer sends a DM on Instagram. Meta delivers the webhook event to an API Gateway endpoint&lt;/li&gt;
&lt;li&gt;The Inbound Handler Lambda validates the webhook, parses the message, and looks up or creates an Amazon Connect Chat session&lt;/li&gt;
&lt;li&gt;The customer's Instagram profile is fetched via the Graph API and cached in DynamoDB&lt;/li&gt;
&lt;li&gt;Text messages and attachments are forwarded into the Connect Chat session via the Participant API&lt;/li&gt;
&lt;li&gt;When an agent replies, Amazon Connect publishes the event to an SNS topic via contact streaming&lt;/li&gt;
&lt;li&gt;The Outbound Handler Lambda picks up the SNS event, looks up the customer's Instagram ID, and sends the reply back through the Instagram Graph API&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Inbound: Instagram → Amazon Connect
&lt;/h2&gt;

&lt;p&gt;When a customer sends a DM to your Instagram Business account, the inbound path handles everything from webhook validation to message delivery.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Webhook Validation and Message Parsing
&lt;/h3&gt;

&lt;p&gt;Meta sends webhook events to your API Gateway &lt;code&gt;/messages&lt;/code&gt; endpoint. The Lambda first handles GET requests for webhook verification — Meta sends a challenge token that must be echoed back with the correct verification token.&lt;/p&gt;

&lt;p&gt;For POST requests (actual messages), the &lt;code&gt;InstagramService&lt;/code&gt; class parses the webhook payload. Instagram webhooks arrive with &lt;code&gt;object: "instagram"&lt;/code&gt; and contain entries with messaging data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InstagramMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sender&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recipient_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;recipient&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;message_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messaging_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mid&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachments&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attachments&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachment&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The service also filters out echo messages — messages sent by your own Instagram account — to prevent infinite loops.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. User Profile Retrieval and Caching
&lt;/h3&gt;

&lt;p&gt;Before routing the message to Connect, the handler fetches the sender's Instagram profile using the Graph API. This provides the agent with context about who they're talking to:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instagram_scoped_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Check in-memory cache first
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;instagram_scoped_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_profiles&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;instagram_scoped_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Check DynamoDB users table
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;users_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TableService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;table_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;USERS_TABLE_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;db_profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;instagram_scoped_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db_profile&lt;/span&gt;

    &lt;span class="c1"&gt;# Fetch from Graph API as last resort
&lt;/span&gt;    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fields&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.instagram.com/v24.0/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;instagram_scoped_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="c1"&gt;# ... fetch and cache
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The profile includes fields like &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;profile_pic&lt;/code&gt;, &lt;code&gt;follower_count&lt;/code&gt;, &lt;code&gt;is_user_follow_business&lt;/code&gt;, and &lt;code&gt;is_verified_user&lt;/code&gt;. Profiles are cached in a DynamoDB table with a 7-day TTL, so repeat conversations skip the API call entirely.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Session Management
&lt;/h3&gt;

&lt;p&gt;The handler checks DynamoDB for an existing chat session using the sender's Instagram-scoped ID:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a session exists, it sends the message using the stored &lt;code&gt;connectionToken&lt;/code&gt;. If the token is expired (AccessDeniedException), it automatically creates a new session.&lt;/li&gt;
&lt;li&gt;If no session exists, it calls &lt;code&gt;StartChatContact&lt;/code&gt; to create a new Amazon Connect Chat, starts contact streaming to the SNS topic, creates a participant connection, and stores everything in DynamoDB.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  4. Attachment Handling (Inbound)
&lt;/h3&gt;

&lt;p&gt;When a customer sends an image, the handler downloads it from Instagram's CDN and uploads it to the Connect Chat session using the Participant API's three-step attachment flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;start_attachment_upload&lt;/code&gt; — creates an upload slot with a pre-signed URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUT&lt;/code&gt; to the pre-signed URL — uploads the binary content&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;complete_attachment_upload&lt;/code&gt; — finalizes the upload&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the download or upload fails, the handler falls back to sending the attachment URL as a text message so the agent still has access to the content.&lt;/p&gt;
&lt;h2&gt;
  
  
  Outbound: Amazon Connect → Instagram
&lt;/h2&gt;

&lt;p&gt;When an agent replies from the Amazon Connect workspace, the outbound path delivers the message back to Instagram.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Streaming Events via SNS
&lt;/h3&gt;

&lt;p&gt;Amazon Connect publishes chat streaming events to an SNS topic. The Outbound Handler Lambda subscribes to this topic and processes three event types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MESSAGE&lt;/code&gt; — text messages from the agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ATTACHMENT&lt;/code&gt; — file attachments sent by the agent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EVENT&lt;/code&gt; — participant join/leave and chat ended events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Messages from the &lt;code&gt;CUSTOMER&lt;/code&gt; role are skipped to avoid echo loops.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Sending Text Messages
&lt;/h3&gt;

&lt;p&gt;For text messages with &lt;code&gt;CUSTOMER&lt;/code&gt; or &lt;code&gt;ALL&lt;/code&gt; visibility, the handler looks up the customer's Instagram-scoped ID and the Instagram Business Account ID from DynamoDB, then sends the reply via the Graph API:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_instagram_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text_message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;instagram_account_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.instagram.com/v24.0/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;instagram_account_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recipient&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recipient_id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text_message&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;access_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# POST to Instagram Graph API
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Sending Attachments
&lt;/h3&gt;

&lt;p&gt;When an agent sends a file from the Connect Chat widget, the handler retrieves a signed URL for the attachment and forwards it to Instagram as a media message. The MIME type determines the Instagram message type:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MIME prefix&lt;/th&gt;
&lt;th&gt;Instagram type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;audio/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audio&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;everything else&lt;/td&gt;
&lt;td&gt;&lt;code&gt;file&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  4. Session Cleanup
&lt;/h3&gt;

&lt;p&gt;When a participant leaves or the chat ends, the handler deletes the connection record from DynamoDB so the next inbound message starts a fresh session.&lt;/p&gt;
&lt;h2&gt;
  
  
  Message Types Supported
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Text&lt;/th&gt;
&lt;th&gt;Images&lt;/th&gt;
&lt;th&gt;Documents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Inbound (customer → agent)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound (agent → customer)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Sending documents from the Instagram user app is not currently possible (instagram app limitations), but customers can receive documents sent by agents from Amazon Connect.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Gets Deployed
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/messages&lt;/code&gt; endpoint (GET &amp;amp; POST)&lt;/td&gt;
&lt;td&gt;API Gateway&lt;/td&gt;
&lt;td&gt;Receives Meta webhook verification and inbound messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Processes Instagram messages and routes them to Amazon Connect Chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound Handler&lt;/td&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;Sends agent replies back to Instagram via the Graph API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Active Connections table&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Tracks open chat sessions (&lt;code&gt;contactId&lt;/code&gt; PK, &lt;code&gt;userId&lt;/code&gt; GSI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instagram Users table&lt;/td&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;Caches Instagram user profiles (TTL-based expiry)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;messages_out&lt;/code&gt; topic&lt;/td&gt;
&lt;td&gt;SNS&lt;/td&gt;
&lt;td&gt;Delivers Amazon Connect streaming events to the Outbound Handler&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instagram-token&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secrets Manager&lt;/td&gt;
&lt;td&gt;Stores the Instagram User Access Token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/meta/instagram/config&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Holds Connect instance ID, contact flow ID, verification token, and Instagram account ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/meta/instagram/webhook/url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SSM Parameter Store&lt;/td&gt;
&lt;td&gt;Stores the deployed API Gateway callback URL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Cost Estimation
&lt;/h2&gt;

&lt;p&gt;Example scenario: 1,000 conversations per month, averaging 10 messages each (5 inbound + 5 outbound), totaling 10,000 messages.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Estimated Monthly Cost&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure (API GW, Lambda, DynamoDB, SNS, Secrets Manager)&lt;/td&gt;
&lt;td&gt;~$0.71&lt;/td&gt;
&lt;td&gt;Negligible at this scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Inbound)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Connect Chat (Outbound)&lt;/td&gt;
&lt;td&gt;$20.00&lt;/td&gt;
&lt;td&gt;5,000 msgs × $0.004/msg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$40.71&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The infrastructure cost is minimal — Amazon Connect Chat messaging is the primary cost driver at $0.004 per message in each direction. See &lt;a href="https://aws.amazon.com/connect/pricing/" rel="noopener noreferrer"&gt;Amazon Connect pricing&lt;/a&gt; for current rates.&lt;/p&gt;

&lt;p&gt;To reduce Connect Chat costs on high-volume conversations, consider adding a &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/whatsapp-eum-connect-chat" rel="noopener noreferrer"&gt;message buffering layer&lt;/a&gt; to aggregate rapid consecutive messages.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deployment Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before getting started you'll need:&lt;/p&gt;
&lt;h3&gt;
  
  
  Instagram Business Account and Meta App
&lt;/h3&gt;

&lt;p&gt;You need an Instagram Business or Creator account connected to a Meta App with the Instagram API configured. The main steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have or create a Meta Business Account&lt;/li&gt;
&lt;li&gt;Create a Meta App and add the Instagram product&lt;/li&gt;
&lt;li&gt;Configure Instagram Login and generate an Instagram User Access Token&lt;/li&gt;
&lt;li&gt;Make sure your Instagram account is a Business or Creator account (personal accounts don't support the Messaging API)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/instagram_setup.md" rel="noopener noreferrer"&gt;Instagram Setup Guide&lt;/a&gt; for detailed step-by-step instructions.&lt;/p&gt;

&lt;p&gt;⚠️ Important: In development mode, your app can only receive messages from Instagram accounts with a role on the Meta App.&lt;/p&gt;
&lt;h3&gt;
  
  
  An Amazon Connect Instance
&lt;/h3&gt;

&lt;p&gt;You need an Amazon Connect instance. If you don't have one yet, you can &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;follow this guide&lt;/a&gt; to create one.&lt;/p&gt;

&lt;p&gt;You'll need the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; of your instance. You can find it in the Amazon Connect console or in the instance ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  A Chat Flow to Handle Messages
&lt;/h3&gt;

&lt;p&gt;Create or have ready the contact flow that defines the user experience. &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/create-contact-flow.html" rel="noopener noreferrer"&gt;Follow this guide&lt;/a&gt; to create an Inbound Contact Flow. The simplest one will work.&lt;/p&gt;

&lt;p&gt;Remember to publish the flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4wb3xzi2aq4pxk55wh3.png" alt="Simple Flow" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take note of the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; and &lt;strong&gt;CONTACT_FLOW_ID&lt;/strong&gt; from the Details tab. The values are in the flow ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID/contact-flow/CONTACT_FLOW_ID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(see the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_connect.md" rel="noopener noreferrer"&gt;Amazon Connect Prerequisites&lt;/a&gt; for more details)&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying with AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Deploy in the same region where your Amazon Connect instance is configured.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Clone the repository and navigate to the project
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-amazon-connect-social-integration.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-amazon-connect-social-integration/instagram-dm-connect-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Deploy with CDK
&lt;/h3&gt;

&lt;p&gt;Follow the instructions in the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;CDK Deployment Guide&lt;/a&gt; for environment setup and deployment commands.&lt;/p&gt;
&lt;h2&gt;
  
  
  Post-deployment Configuration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Update the Instagram Access Token in Secrets Manager
&lt;/h3&gt;

&lt;p&gt;The stack creates a Secrets Manager secret named &lt;a href="https://console.aws.amazon.com/secretsmanager/secret?name=instagram-token" rel="noopener noreferrer"&gt;&lt;code&gt;instagram-token&lt;/code&gt;&lt;/a&gt; with a placeholder value. Update it with your actual Instagram User Access Token.&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/instagram_setup.md" rel="noopener noreferrer"&gt;Instagram Setup Guide&lt;/a&gt; for how to generate this token.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Update the SSM Configuration Parameter
&lt;/h3&gt;

&lt;p&gt;After deployment,  go to &lt;a href="https://console.aws.amazon.com/systems-manager/parameters" rel="noopener noreferrer"&gt;AWS Systems Manager - Parameter Store&lt;/a&gt; and update the SSM parameter  &lt;code&gt;/meta/instagram/config&lt;/code&gt; with your Amazon Connect and Instagram details:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instance_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your Amazon Connect Instance ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contact_flow_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The ID of the Inbound Contact Flow for chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INSTAGRAM_VERIFICATION_TOKEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A secret string you choose — must match what you enter in the Meta webhook config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instagram_account_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your Instagram Business Account ID (see note below)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To find your &lt;code&gt;instagram_account_id&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In your Meta App Dashboard → Instagram → API Setup with Instagram Login → expand "1. Generate access tokens" → the ID is underneath the linked Instagram account&lt;/li&gt;
&lt;li&gt;Or call the Graph API:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; GET &lt;span class="s2"&gt;"https://graph.instagram.com/me?fields=id,username,account_type,user_id&amp;amp;access_token=YOUR_IG_ACCESS_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;user_id&lt;/code&gt; field in the response is your &lt;code&gt;instagram_account_id&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Configure the Webhook in Meta App Dashboard
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to your Meta App Dashboard → Instagram → API Setup with Instagram Login → Webhooks&lt;/li&gt;
&lt;li&gt;Set the &lt;strong&gt;Callback URL&lt;/strong&gt; to the API Gateway URL. You can find it in the SSM parameter &lt;code&gt;/meta/instagram/webhook/url&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set the &lt;strong&gt;Verify Token&lt;/strong&gt; to the same value you used for &lt;code&gt;INSTAGRAM_VERIFICATION_TOKEN&lt;/code&gt; above&lt;/li&gt;
&lt;li&gt;Subscribe to the &lt;code&gt;messages&lt;/code&gt; webhook field&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For full details, see the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/instagram_setup.md" rel="noopener noreferrer"&gt;Instagram Setup Guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Go to your Amazon Connect instance and &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/launch-ccp.html" rel="noopener noreferrer"&gt;open the Contact Control Panel (CCP)&lt;/a&gt;.&lt;/p&gt;





&lt;p&gt;Try these scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send a DM to your Instagram Business account from another Instagram account — it should appear as a new chat contact in the CCP&lt;/li&gt;
&lt;li&gt;Reply from the CCP — the response should arrive in the customer's Instagram DMs&lt;/li&gt;
&lt;li&gt;Send an image from Instagram — it should appear as an image attachment in the agent's chat&lt;/li&gt;
&lt;li&gt;From the agent side, send an image or document — it should appear in the customer's Instagram DMs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Important Considerations around Instagram
&lt;/h2&gt;
&lt;h3&gt;
  
  
  24-Hour Messaging Window
&lt;/h3&gt;

&lt;p&gt;Instagram has a &lt;strong&gt;24-hour standard messaging window&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After a user sends a message, your account has 24 hours to respond&lt;/li&gt;
&lt;li&gt;Outside this window, messaging is restricted&lt;/li&gt;
&lt;li&gt;Each new user message reopens the 24-hour window&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Human Agent Messaging Window
&lt;/h3&gt;

&lt;p&gt;Instagram provides a &lt;strong&gt;7-day human agent messaging window&lt;/strong&gt; for conversations that are escalated to a human agent. This extended window allows agents more time to resolve complex issues.&lt;/p&gt;
&lt;h3&gt;
  
  
  App Review
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;development mode&lt;/strong&gt;, your app can only receive messages from Instagram accounts with a role on the Meta App (Admin, Developer, Tester)&lt;/li&gt;
&lt;li&gt;For production use with real customers, you need to submit for &lt;a href="https://developers.facebook.com/docs/app-review" rel="noopener noreferrer"&gt;App Review&lt;/a&gt; and request the required permissions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Rate Limits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The Instagram Messaging API has rate limits based on your app's usage tier&lt;/li&gt;
&lt;li&gt;Monitor API response headers for rate limit information&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;This solution handles the core Instagram-to-Connect messaging flow. Some ideas to extend it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add support for Instagram Stories mentions and replies&lt;/li&gt;
&lt;li&gt;Use Amazon Bedrock to analyze inbound images and provide agents with context before they respond&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/agentic-self-service.html" rel="noopener noreferrer"&gt;Amazon Connect AI Agents&lt;/a&gt; for agentic self-service, letting customers resolve common issues without waiting for a human agent&lt;/li&gt;
&lt;li&gt;Combine with the &lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/tree/main/facebook-messenger-connect-chat" rel="noopener noreferrer"&gt;Facebook Messenger integration&lt;/a&gt; to handle both Meta channels from a single Amazon Connect instance&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Leverage Amazon Connect Customer Profiles
&lt;/h3&gt;

&lt;p&gt;This solution already fetches Instagram profile data (name, username, profile picture, follower count) and passes it as contact attributes. You can take this further by integrating with &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles.html" rel="noopener noreferrer"&gt;Amazon Connect Customer Profiles&lt;/a&gt; to give agents a unified view of the customer across channels. Then in your Contact Flow, use the &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles-block.html" rel="noopener noreferrer"&gt;Customer Profiles block&lt;/a&gt; to retrieve the profile and display it in the agent workspace. The agent sees the customer's name, Instagram handle, follower count, and any previous interaction history — all before they even type a reply.&lt;/p&gt;
&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration" rel="noopener noreferrer"&gt;Project Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Amazon Connect Administrator Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/messaging-api" rel="noopener noreferrer"&gt;Instagram Messaging API — Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/api-reference" rel="noopener noreferrer"&gt;Instagram Graph API — User Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/graph-api/webhooks/getting-started" rel="noopener noreferrer"&gt;Meta Webhooks — Getting Started&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/aws-samples/sample-amazon-connect-social-integration/blob/main/instagram_setup.md" rel="noopener noreferrer"&gt;Instagram Setup Guide&lt;/a&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>instagram</category>
      <category>amazonconnect</category>
      <category>cdk</category>
      <category>chat</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Thu, 19 Mar 2026 05:54:18 +0000</pubDate>
      <link>https://dev.to/ensamblador/-4knh</link>
      <guid>https://dev.to/ensamblador/-4knh</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/vsenger" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3594510%2Fd85d5ead-f67c-474b-8864-ae2185615ec9.png" alt="vsenger"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/vsenger/building-your-own-ai-powered-streamdeck-with-rebutton-4ol5" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Building Your Own AI-Powered StreamDeck with re:Button&lt;/h2&gt;
      &lt;h3&gt;Vinicius Senger ・ Feb 28&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#diy&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#automation&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ai&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>diy</category>
      <category>opensource</category>
      <category>automation</category>
      <category>ai</category>
    </item>
    <item>
      <title>Manejo de Archivos Adjuntos (y notas de voz) entre WhatsApp y Amazon Connect</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Mon, 02 Mar 2026 17:14:34 +0000</pubDate>
      <link>https://dev.to/ensamblador/manejo-de-archivos-adjuntos-y-notas-de-voz-entre-whatsapp-y-amazon-connect-15jj</link>
      <guid>https://dev.to/ensamblador/manejo-de-archivos-adjuntos-y-notas-de-voz-entre-whatsapp-y-amazon-connect-15jj</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;




&lt;blockquote&gt;
&lt;p&gt;Aprende a manejar archivos adjuntos en ambas direcciones entre WhatsApp y Amazon Connect — imágenes, documentos, audio y video. Esta guía paso a paso cubre la arquitectura completa usando AWS CDK, AWS Lambda, AWS End User Messaging Social, Amazon S3 y Amazon Connect. Desde descargar medios de WhatsApp hasta subirlos al Chat de Connect, reenviar archivos del agente de vuelta a WhatsApp, y procesar notas de voz con conversión de formato y transcripción en tiempo real usando Amazon Transcribe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14ghw6f077tqkv6mdp5k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14ghw6f077tqkv6mdp5k.gif" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Los mensajes de texto son solo el comienzo. Los clientes envían fotos de productos dañados, PDFs de facturas, notas de voz explicando su problema, y a veces incluso videos. Si tu integración de WhatsApp con Amazon Connect solo maneja texto, te estás perdiendo una gran parte de la conversación.&lt;/p&gt;

&lt;p&gt;En este blog, aprenderás a manejar archivos adjuntos en ambas direcciones — del cliente al agente y del agente al cliente — incluyendo un pipeline para convertir y transcribir notas de voz. Esto habilita casos de uso avanzados como reclamos de seguros con evidencia fotográfica, instrucciones de voz transcritas para el agente, e intercambio de documentos sin salir del chat.&lt;/p&gt;

&lt;p&gt;Consulta el código en &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat" rel="noopener noreferrer"&gt;https://github.com/aws-samples&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que vas a construir
&lt;/h2&gt;

&lt;p&gt;Una capa de manejo bidireccional de archivos adjuntos entre WhatsApp y Amazon Connect que:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detecta y descarga medios de los mensajes entrantes de WhatsApp (imágenes, documentos, audio, video)&lt;/li&gt;
&lt;li&gt;Convierte las notas de voz de WhatsApp (OGG/Opus) a WAV para compatibilidad con Connect&lt;/li&gt;
&lt;li&gt;Transcribe las notas de voz en tiempo real usando Amazon Transcribe Streaming&lt;/li&gt;
&lt;li&gt;Agrega esos archivos a la sesión de Chat de Amazon Connect para que los agentes puedan verlos&lt;/li&gt;
&lt;li&gt;Reenvía los archivos enviados por los agentes desde el widget de Chat de Connect de vuelta a WhatsApp&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El resultado final: agentes y clientes pueden intercambiar archivos de forma natural, y las notas de voz llegan tanto como audio reproducible como texto legible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arquitectura
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftnhl40edyqlv1f3uy0tf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftnhl40edyqlv1f3uy0tf.png" alt="Diagrama de Arquitectura"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Así funciona el flujo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Un cliente envía un archivo o medio por WhatsApp. La Lambda del manejador de entrada lo descarga a S3 usando el AWS SDK&lt;/li&gt;
&lt;li&gt;Si el archivo es una nota de voz (OGG), se convierte a WAV usando ffmpeg en una Lambda separada&lt;/li&gt;
&lt;li&gt;El archivo se sube a la sesión de Chat de Amazon Connect a través de la API de Participante&lt;/li&gt;
&lt;li&gt;Cuando un agente envía un archivo desde el widget de Chat de Connect, el manejador de salida detecta el evento &lt;code&gt;ATTACHMENT&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;El manejador obtiene una URL firmada del archivo y lo envía a WhatsApp como mensaje de medios&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Tipos de Mensajes de WhatsApp
&lt;/h2&gt;

&lt;p&gt;Los mensajes de WhatsApp no son solo texto. El payload del webhook de Meta incluye un campo &lt;code&gt;type&lt;/code&gt; que indica qué tipo de contenido envió el cliente. Cada tipo de medio lleva su contenido en un campo dedicado dentro del objeto del mensaje:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tipo&lt;/th&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Mensaje de texto plano&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fotos, capturas de pantalla, memes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;document&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;document&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PDFs, hojas de cálculo, documentos de Word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;audio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Notas de voz (formato OGG/Opus)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Clips de video&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sticker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sticker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stickers de WhatsApp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reacciones con emoji a mensajes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No todos son útiles en un contexto de atención al cliente. Los stickers y las reacciones típicamente agregan ruido en lugar de valor, por lo que la solución los hace configurables — puedes ignorarlos a través del parámetro SSM:&lt;/p&gt;

&lt;h3&gt;
  
  
  Tipos de Adjuntos Soportados en este proyecto
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dirección&lt;/th&gt;
&lt;th&gt;Imágenes&lt;/th&gt;
&lt;th&gt;Documentos&lt;/th&gt;
&lt;th&gt;Audio&lt;/th&gt;
&lt;th&gt;Video&lt;/th&gt;
&lt;th&gt;Stickers&lt;/th&gt;
&lt;th&gt;Reacciones&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entrada (WhatsApp → Connect)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (convertido + transcrito)&lt;/td&gt;
&lt;td&gt;N/I&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;N/I&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Salida (Connect → WhatsApp)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;N/I: No implementado aquí, pero factible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_reactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_stickers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Para los tipos de medios (&lt;code&gt;image&lt;/code&gt;, &lt;code&gt;document&lt;/code&gt;, &lt;code&gt;audio&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt;), el payload del mensaje incluye un &lt;code&gt;media_id&lt;/code&gt; que usas para descargar el contenido real del archivo. El archivo en sí no está en el webhook — necesitas obtenerlo por separado.&lt;/p&gt;
&lt;h2&gt;
  
  
  Entrada: WhatsApp → Amazon Connect
&lt;/h2&gt;

&lt;p&gt;Cuando un cliente envía un archivo por WhatsApp, la Lambda del manejador de entrada (&lt;code&gt;whatsapp_event_handler&lt;/code&gt;) lo procesa en tres etapas: detección, descarga y subida.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Detección y Descarga
&lt;/h3&gt;

&lt;p&gt;La clase &lt;code&gt;WhatsappMessage&lt;/code&gt; inspecciona cada mensaje entrante en busca de campos de medios. Verifica &lt;code&gt;audio&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;document&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt; y &lt;code&gt;sticker&lt;/code&gt; en ese orden:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;video&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;video&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# reacciones no implementadas
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;# Descargar usando la API de Social Messaging
&lt;/span&gt;    &lt;span class="n"&gt;media_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;download_media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;media_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;phone_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_number_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;media_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ATTACHMENT_PREFIX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Leer contenido binario desde S3
&lt;/span&gt;    &lt;span class="n"&gt;binary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_s3_file_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;media_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El método &lt;code&gt;download_media()&lt;/code&gt; llama a la API de End User Messaging Social (&lt;code&gt;get_whatsapp_message_media&lt;/code&gt;), que descarga el archivo desde Meta a un bucket de S3. El archivo queda en &lt;code&gt;s3://&amp;lt;bucket&amp;gt;/&amp;lt;prefix&amp;gt;&amp;lt;media_id&amp;gt;.&amp;lt;extension&amp;gt;&lt;/code&gt; donde la extensión se deriva del tipo MIME.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Subida al Chat de Amazon Connect
&lt;/h3&gt;

&lt;p&gt;Una vez que el archivo está en S3 y su contenido binario está cargado, la función &lt;code&gt;process_attachment()&lt;/code&gt; lo sube a la sesión activa de Chat de Connect usando la API de Participante. Este es un proceso de tres pasos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;start_attachment_upload&lt;/code&gt; — crea un slot de subida, retorna una URL pre-firmada y un ID de adjunto&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUT&lt;/code&gt; a la URL pre-firmada — sube el contenido binario&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;complete_attachment_upload&lt;/code&gt; — finaliza la subida
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attach_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ConnectionToken&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Paso 1: Crear slot de subida
&lt;/span&gt;    &lt;span class="n"&gt;attachResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;participant_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_attachment_upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fileType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;AttachmentSizeInBytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fileSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;AttachmentName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ConnectionToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ConnectionToken&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Paso 2: Subir a la URL pre-firmada
&lt;/span&gt;    &lt;span class="n"&gt;upload_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attachResponse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UploadMetadata&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;upload_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attachResponse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UploadMetadata&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HeadersToInclude&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Paso 3: Finalizar
&lt;/span&gt;    &lt;span class="n"&gt;participant_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete_attachment_upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;AttachmentIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;attachResponse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AttachmentId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
        &lt;span class="n"&gt;ConnectionToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ConnectionToken&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Salida: Amazon Connect → WhatsApp
&lt;/h2&gt;

&lt;p&gt;Cuando un agente envía un archivo desde el widget de Chat de Connect, la Lambda del manejador de salida (&lt;code&gt;connect_event_handler&lt;/code&gt;) lo captura y lo reenvía a WhatsApp.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Detección de Adjuntos
&lt;/h3&gt;

&lt;p&gt;Amazon Connect publica eventos de streaming a un tema de SNS. El manejador verifica el campo &lt;code&gt;Type&lt;/code&gt; en cada evento:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MESSAGE&lt;/code&gt; — mensaje de texto&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ATTACHMENT&lt;/code&gt; — archivo adjunto&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EVENT&lt;/code&gt; — eventos de entrada/salida de participantes&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. Obtención de URL Firmada
&lt;/h3&gt;

&lt;p&gt;Para cada adjunto con &lt;code&gt;Status: APPROVED&lt;/code&gt;, el manejador busca el número de teléfono del cliente y el número del sistema en DynamoDB usando el &lt;code&gt;contactId&lt;/code&gt;, luego obtiene una URL de descarga temporal:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_signed_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;participant_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;AttachmentId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ConnectionToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;connectionToken&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Envío a WhatsApp
&lt;/h3&gt;

&lt;p&gt;El manejador mapea el tipo MIME al tipo de mensaje de WhatsApp correspondiente y envía el archivo usando la URL firmada como enlace de medios — no es necesario volver a subir el archivo:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_whatsapp_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attachment_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_file_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# image, video, audio, o document
&lt;/span&gt;    &lt;span class="n"&gt;message_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messaging_product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;whatsapp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recipient_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;individual&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;+&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;message_object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;attachment_url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;message_object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filename&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;

    &lt;span class="n"&gt;socialessaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_whatsapp_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;originationPhoneNumberId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;phone_number_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;metaApiVersion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;meta_api_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message_object&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Prefijo MIME&lt;/th&gt;
&lt;th&gt;Tipo WhatsApp&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;audio/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audio&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;todo lo demás&lt;/td&gt;
&lt;td&gt;&lt;code&gt;document&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Para los tipos &lt;code&gt;document&lt;/code&gt;, se preserva el nombre original del archivo para que el cliente vea un nombre significativo en su chat de WhatsApp.&lt;/p&gt;
&lt;h2&gt;
  
  
  Caso Especial. Procesamiento de Notas de Voz
&lt;/h2&gt;

&lt;p&gt;Más allá del simple reenvío de archivos, los adjuntos pueden procesarse para habilitar casos de uso avanzados. El ejemplo más interesante en esta solución es el manejo de notas de voz — conversión de formatos de audio y transcripción de voz a texto.&lt;/p&gt;
&lt;h3&gt;
  
  
  El Problema con las Notas de Voz
&lt;/h3&gt;

&lt;p&gt;Las notas de voz de WhatsApp llegan en formato OGG/Opus. El Chat de Amazon Connect &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/feature-limits.html" rel="noopener noreferrer"&gt;no soporta archivos OGG como adjuntos&lt;/a&gt;. Si intentas subir un archivo OGG, será rechazado. Por eso necesitas un paso de conversión.&lt;/p&gt;
&lt;h3&gt;
  
  
  Conversión OGG → WAV
&lt;/h3&gt;

&lt;p&gt;Una función Lambda dedicada (&lt;code&gt;convert_to_wav&lt;/code&gt;) maneja la conversión de formato usando ffmpeg. Después de la conversión, el manejador de entrada lee el contenido WAV desde S3 y lo sube al Chat de Connect como &lt;code&gt;voice.wav&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Transcripción en Tiempo Real con Amazon Transcribe Streaming
&lt;/h3&gt;

&lt;p&gt;El archivo OGG original también se envía a una Lambda &lt;code&gt;transcribe_audio&lt;/code&gt; para la conversión de voz a texto. Esta usa Amazon Transcribe Streaming — no la API por lotes — para obtener resultados en tiempo casi real.&lt;/p&gt;
&lt;h2&gt;
  
  
  Más Allá de las Notas de Voz: Ideas de Procesamiento Avanzado
&lt;/h2&gt;

&lt;p&gt;El mismo patrón — interceptar, procesar, reenviar — puede extenderse a otros tipos de adjuntos para casos de uso avanzados:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Comprensión de imágenes&lt;/strong&gt;: Usa Amazon Bedrock o Amazon Rekognition para analizar fotos. ¿Un cliente envía una foto de un producto dañado? Extrae una descripción y adjúntala al chat junto con la imagen. Útil para reclamos de seguros o solicitudes de garantía.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Análisis de video&lt;/strong&gt;: Extrae fotogramas clave de los adjuntos de video y pásalos por modelos multimodales para su comprensión. ¿Un cliente envía un video de un dispositivo con fallas? Resume el problema para el agente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extracción de documentos&lt;/strong&gt;: Usa Amazon Textract o Modelos de Base multimodales para extraer texto de documentos escaneados, facturas o formularios. Pre-llena los detalles del caso antes de que el agente abra el chat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detección de idioma y traducción&lt;/strong&gt;: Detecta el idioma de las notas de voz o el texto en imágenes y tradúcelos antes de reenviarlos al agente.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El manejador de entrada está diseñado para ser extensible — puedes agregar pasos de procesamiento entre la descarga y la subida a Connect sin cambiar el flujo general.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerrequisitos de Despliegue
&lt;/h2&gt;

&lt;p&gt;Antes de comenzar necesitarás:&lt;/p&gt;
&lt;h3&gt;
  
  
  Cuenta de WhatsApp Business
&lt;/h3&gt;

&lt;p&gt;Para empezar, necesitas crear una nueva cuenta de WhatsApp Business (WABA) o migrar una existente a AWS. Los pasos principales se describen &lt;a href="https://docs.aws.amazon.com/social-messaging/latest/userguide/getting-started.html" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;. En resumen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tener o crear una cuenta de Meta Business&lt;/li&gt;
&lt;li&gt;Acceder a la consola de AWS End User Messaging Social y vincular tu cuenta de negocio a través del portal integrado de Facebook&lt;/li&gt;
&lt;li&gt;Asegurarte de tener un número de teléfono que pueda recibir verificación por SMS/voz y agregarlo a WhatsApp&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⚠️ Importante: No uses tu número personal de WhatsApp para esto.&lt;/p&gt;
&lt;h3&gt;
  
  
  Una Instancia de Amazon Connect
&lt;/h3&gt;

&lt;p&gt;Necesitas una instancia de Amazon Connect. Si aún no tienes una, puedes &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;seguir esta guía&lt;/a&gt; para crear una.&lt;/p&gt;

&lt;p&gt;Necesitarás el &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; de tu instancia. Puedes encontrarlo en la consola de Amazon Connect o en el ARN de la instancia:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Un Flujo de Chat para Manejar Mensajes
&lt;/h3&gt;

&lt;p&gt;Crea o ten listo el flujo de contacto que define la experiencia del usuario. &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/create-contact-flow.html" rel="noopener noreferrer"&gt;Sigue esta guía&lt;/a&gt; para crear un flujo de contacto de entrada (Inbound Contact Flow). El más sencillo funcionará.&lt;/p&gt;

&lt;p&gt;Recuerda publicar el flujo.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qp65ezw0uaovvsvss7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qp65ezw0uaovvsvss7o.png" alt="Flujo Simple"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Toma nota del &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; y &lt;strong&gt;CONTACT_FLOW_ID&lt;/strong&gt; desde la pestaña de Detalles. Los valores están en el ARN del flujo:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID/contact-flow/CONTACT_FLOW_ID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(consulta los &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/general_connect_eum.md" rel="noopener noreferrer"&gt;Prerrequisitos de WhatsApp / Connect&lt;/a&gt; para más detalles)&lt;/p&gt;
&lt;h3&gt;
  
  
  Importante: Habilitar Adjuntos en la Instancia de Amazon Connect
&lt;/h3&gt;

&lt;p&gt;Sigue &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/enable-attachments.html" rel="noopener noreferrer"&gt;estos pasos&lt;/a&gt; para habilitar el intercambio de adjuntos.&lt;/p&gt;
&lt;h2&gt;
  
  
  Despliegue con AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Despliega en la misma región donde están configurados tus números de WhatsApp en AWS End User Messaging.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Clona el repositorio y navega al proyecto
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-whatsapp-end-user-messaging-connect-chat/whatsapp-eum-connect-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Despliega con CDK
&lt;/h3&gt;

&lt;p&gt;Sigue las instrucciones en la &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;Guía de Despliegue con CDK&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuración Post-despliegue
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Paso 1: Actualizar el Parámetro SSM
&lt;/h3&gt;

&lt;p&gt;Después del despliegue, actualiza el parámetro SSM &lt;code&gt;/whatsapp_eum_connect_chat/config&lt;/code&gt; con los detalles de tu Amazon Connect:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"instance_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;tu-connect-instance-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contact_flow_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;tu-contact-flow-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chat_duration_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_reactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_stickers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parámetro&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instance_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;El ID de tu instancia de Amazon Connect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contact_flow_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;El ID del flujo de contacto de entrada para chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chat_duration_minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cuánto tiempo permanece activa la sesión de chat (por defecto: 60)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ignore_reactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Si se ignoran las reacciones de WhatsApp (por defecto: yes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ignore_stickers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Si se ignoran los stickers de WhatsApp (por defecto: yes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Paso 2: Agregar el Destino de Eventos
&lt;/h3&gt;

&lt;p&gt;Después de desplegar el stack, usa el tema SNS creado como destino de eventos en la consola de AWS End User Messaging Social.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ve a AWS Systems Manager Parameter Store y copia el valor de &lt;code&gt;/whatsapp_eum_connect_chat/topic/in&lt;/code&gt; (comienza con &lt;code&gt;arn:aws:sns&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkuyintfmpqjwt9hgmii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkuyintfmpqjwt9hgmii.png" alt="Parámetro del Tema"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;En la consola de AWS End User Messaging Social, selecciona el destino &lt;strong&gt;Amazon SNS&lt;/strong&gt; y pega el &lt;strong&gt;Topic ARN&lt;/strong&gt; del paso anterior 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87sv4kmtsfe863octr3n.png" alt="Configuración SNS EUM"&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Paso 3: Configurar el Idioma de Transcripción (Opcional)
&lt;/h3&gt;

&lt;p&gt;El idioma de transcripción está configurado en &lt;code&gt;es-US&lt;/code&gt; (Español) por defecto. Para cambiarlo, edita el parámetro &lt;code&gt;language_code&lt;/code&gt; en &lt;code&gt;lambdas/code/transcribe_audio/transcribe.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transcribe_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_stream_transcription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;language_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en-US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Cambia al idioma deseado
&lt;/span&gt;    &lt;span class="n"&gt;media_sample_rate_hz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;48000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;media_encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ogg-opus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Pruebas
&lt;/h2&gt;

&lt;p&gt;Ve a tu instancia de Amazon Connect y &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/launch-ccp.html" rel="noopener noreferrer"&gt;abre el Panel de Control de Contactos (CCP)&lt;/a&gt;. Envía un mensaje de WhatsApp al número de End User Messaging Social.&lt;/p&gt;





&lt;p&gt;Prueba estos escenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Envía una foto — debería aparecer como un adjunto de imagen en el chat del agente&lt;/li&gt;
&lt;li&gt;Envía un PDF — debería aparecer como un adjunto de documento&lt;/li&gt;
&lt;li&gt;Envía una nota de voz — debería llegar como un archivo de audio WAV más una transcripción de texto&lt;/li&gt;
&lt;li&gt;Desde el lado del agente, envía una imagen o documento — debería aparecer en el chat de WhatsApp del cliente&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Próximos Pasos
&lt;/h2&gt;

&lt;p&gt;Esta solución maneja el flujo principal de adjuntos. Algunas ideas para extenderla:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modelo de Base multimodal para análisis de imágenes en fotos entrantes (por ejemplo, evaluación de daños para reclamos)&lt;/li&gt;
&lt;li&gt;Implementar soporte para adjuntos de video entrantes&lt;/li&gt;
&lt;li&gt;Soportar múltiples idiomas de transcripción con detección automática de idioma&lt;/li&gt;
&lt;li&gt;Combinar con la solución de &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/whatsapp-eum-connect-chat" rel="noopener noreferrer"&gt;Buffering de Mensajes&lt;/a&gt; para agregar mensajes rápidos y la solución de &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/agent-initiated-whatsapp" rel="noopener noreferrer"&gt;WhatsApp Iniciado por el Agente&lt;/a&gt; para comunicación proactiva completa&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat" rel="noopener noreferrer"&gt;Repositorio del Proyecto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Guía del Administrador de Amazon Connect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/feature-limits.html" rel="noopener noreferrer"&gt;Amazon Connect Chat — Tipos de Adjuntos Soportados&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect-participant/latest/APIReference/API_StartAttachmentUpload.html" rel="noopener noreferrer"&gt;API de Participante de Amazon Connect — StartAttachmentUpload&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/social-messaging/latest/APIReference/API_SendWhatsAppMessage.html" rel="noopener noreferrer"&gt;AWS End User Messaging Social — API SendWhatsAppMessage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/social-messaging/latest/userguide/what-is-service.html" rel="noopener noreferrer"&gt;Guía del Usuario de AWS End User Messaging Social&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/awslabs/amazon-transcribe-streaming-sdk" rel="noopener noreferrer"&gt;SDK de Amazon Transcribe Streaming&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media" rel="noopener noreferrer"&gt;Plataforma WhatsApp Business — Mensajes de Medios&lt;/a&gt;


&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;




&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>amazonconnect</category>
      <category>cdk</category>
      <category>whatsapp</category>
      <category>attachments</category>
    </item>
    <item>
      <title>Handling Attachments (and voice notes) between Whatsapp and Amazon Connect.</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Mon, 02 Mar 2026 16:41:39 +0000</pubDate>
      <link>https://dev.to/ensamblador/handling-attachments-and-voice-notes-between-whatsapp-and-amazon-connect-1m16</link>
      <guid>https://dev.to/ensamblador/handling-attachments-and-voice-notes-between-whatsapp-and-amazon-connect-1m16</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;




&lt;blockquote&gt;
&lt;p&gt;Learn how to handle file attachments in both directions between WhatsApp and Amazon Connect — images, documents, audio, and video. This step-by-step guide covers the full architecture using AWS CDK, AWS Lambda, AWS End User Messaging Social, Amazon S3, and Amazon Connect. From downloading WhatsApp media to uploading it into Connect Chat, forwarding agent files back to WhatsApp, and processing voice notes with format conversion and real-time transcription using Amazon Transcribe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14ghw6f077tqkv6mdp5k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F14ghw6f077tqkv6mdp5k.gif" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Text messages are just the beginning. Customers send photos of damaged products, PDFs of invoices, voice notes explaining their issue, and sometimes even videos. If your WhatsApp integration with Amazon Connect only handles text, you're missing a huge part of the conversation.&lt;/p&gt;

&lt;p&gt;In this blog, you'll learn how to handle file attachments in both directions — customer to agent and agent to customer — including a pipeline for converting and transcribing voice notes using AWS Lambda. This enables advanced use cases like insurance claims with photo evidence, voice instructions transcribed for the agent, and document exchange without leaving the chat.&lt;/p&gt;

&lt;p&gt;Check out the code at &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat" rel="noopener noreferrer"&gt;https://github.com/aws-samples&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll build
&lt;/h2&gt;

&lt;p&gt;A bidirectional attachment handling layer between WhatsApp and Amazon Connect that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detects and downloads media from incoming WhatsApp messages (images, documents, audio, video)&lt;/li&gt;
&lt;li&gt;Converts WhatsApp voice notes (OGG/Opus) to WAV for Connect compatibility&lt;/li&gt;
&lt;li&gt;Transcribes voice notes in real time using Amazon Transcribe Streaming&lt;/li&gt;
&lt;li&gt;Add those files into the Amazon Connect Chat session so agents can see them&lt;/li&gt;
&lt;li&gt;Forwards files sent by agents from the Connect Chat widget back to WhatsApp&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The end result: agents and customers can exchange files naturally, and voice notes arrive both as playable audio and as readable text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomrlkhe1huo744gwbqw0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomrlkhe1huo744gwbqw0.gif" alt="Architecture Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how it flows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A customer sends a file or media on WhatsApp. The inbound handler Lambda downloads it to S3 using AWS SDK.&lt;/li&gt;
&lt;li&gt;If the file is a voice note (OGG), it's converted to WAV using ffmpeg in a separate Lambda&lt;/li&gt;
&lt;li&gt;The file is uploaded to the Amazon Connect Chat session via the Participant API&lt;/li&gt;
&lt;li&gt;When an agent sends a file from the Connect Chat widget, the outbound handler detects the &lt;code&gt;ATTACHMENT&lt;/code&gt; event&lt;/li&gt;
&lt;li&gt;The handler retrieves a signed URL for the file and sends it to WhatsApp as a media message&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Understanding WhatsApp Message Types
&lt;/h2&gt;

&lt;p&gt;WhatsApp messages aren't just text. The webhook payload from Meta includes a &lt;code&gt;type&lt;/code&gt; field that tells you what kind of content the customer sent. Each media type carries its content in a dedicated field within the message object:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plain text message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Photos, screenshots, memes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;document&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;document&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;PDFs, spreadsheets, Word docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;audio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Voice notes (OGG/Opus format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Video clips&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sticker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sticker&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;WhatsApp stickers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;reaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Emoji reactions to messages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Not all of these are useful in a customer service context. Stickers and reactions typically add noise rather than value, so the solution makes them configurable — you can ignore them via the SSM parameter:&lt;/p&gt;

&lt;h3&gt;
  
  
  Supported Attachment Types in this project
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Direction&lt;/th&gt;
&lt;th&gt;Images&lt;/th&gt;
&lt;th&gt;Documents&lt;/th&gt;
&lt;th&gt;Audio&lt;/th&gt;
&lt;th&gt;Video&lt;/th&gt;
&lt;th&gt;Stickers&lt;/th&gt;
&lt;th&gt;Reactions&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Inbound (WhatsApp → Connect)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (converted + transcribed)&lt;/td&gt;
&lt;td&gt;N/I&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;N/I&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound (Connect → WhatsApp)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;N/I: Not implemented here, but feasible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_reactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_stickers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For media types (&lt;code&gt;image&lt;/code&gt;, &lt;code&gt;document&lt;/code&gt;, &lt;code&gt;audio&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt;), the message payload includes a &lt;code&gt;media_id&lt;/code&gt; that you use to download the actual file content. The file itself isn't in the webhook — you need to fetch it separately.&lt;/p&gt;
&lt;h2&gt;
  
  
  Inbound: WhatsApp → Amazon Connect
&lt;/h2&gt;

&lt;p&gt;When a customer sends a file on WhatsApp, the inbound handler Lambda (&lt;code&gt;whatsapp_event_handler&lt;/code&gt;) processes it through three stages: detection, download, and upload.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Detection and Download
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;WhatsappMessage&lt;/code&gt; class inspects each incoming message for media fields. It checks for &lt;code&gt;audio&lt;/code&gt;, &lt;code&gt;image&lt;/code&gt;, &lt;code&gt;document&lt;/code&gt;, &lt;code&gt;video&lt;/code&gt;, and &lt;code&gt;sticker&lt;/code&gt; in that order:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;video&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;video&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sticker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# reactions not implemented
&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="c1"&gt;# Download using the Social Messaging API
&lt;/span&gt;    &lt;span class="n"&gt;media_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;download_media&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;media_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;phone_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone_number_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;bucket_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;BUCKET_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;media_prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ATTACHMENT_PREFIX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Read binary content from S3
&lt;/span&gt;    &lt;span class="n"&gt;binary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_s3_file_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;media_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;location&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;download_media()&lt;/code&gt; method calls the End User Messaging Social API (&lt;code&gt;get_whatsapp_message_media&lt;/code&gt;), which downloads the file from Meta into an S3 bucket. The file lands at &lt;code&gt;s3://&amp;lt;bucket&amp;gt;/&amp;lt;prefix&amp;gt;&amp;lt;media_id&amp;gt;.&amp;lt;extension&amp;gt;&lt;/code&gt; where the extension is derived from the MIME type.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Upload to Amazon Connect Chat
&lt;/h3&gt;

&lt;p&gt;Once the file is in S3 and its binary content is loaded, the &lt;code&gt;process_attachment()&lt;/code&gt; function uploads it to the active Connect Chat session using the Participant API. This is a three-step process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;start_attachment_upload&lt;/code&gt; — creates an upload slot, returns a pre-signed URL and attachment ID&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUT&lt;/code&gt; to the pre-signed URL — uploads the binary content&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;complete_attachment_upload&lt;/code&gt; — finalizes the upload
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;attach_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ConnectionToken&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Step 1: Create upload slot
&lt;/span&gt;    &lt;span class="n"&gt;attachResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;participant_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_attachment_upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fileType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;AttachmentSizeInBytes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fileSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;AttachmentName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ConnectionToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ConnectionToken&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 2: Upload to pre-signed URL
&lt;/span&gt;    &lt;span class="n"&gt;upload_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attachResponse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UploadMetadata&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;upload_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fileContents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attachResponse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;UploadMetadata&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;HeadersToInclude&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Step 3: Finalize
&lt;/span&gt;    &lt;span class="n"&gt;participant_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete_attachment_upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;AttachmentIds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;attachResponse&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AttachmentId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
        &lt;span class="n"&gt;ConnectionToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ConnectionToken&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Outbound: Amazon Connect → WhatsApp
&lt;/h2&gt;

&lt;p&gt;When an agent sends a file from the Connect Chat widget, the outbound handler Lambda (&lt;code&gt;connect_event_handler&lt;/code&gt;) picks it up and forwards it to WhatsApp.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Attachment Detection
&lt;/h3&gt;

&lt;p&gt;Amazon Connect publishes streaming events to an SNS topic. The handler checks the &lt;code&gt;Type&lt;/code&gt; field in each event:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MESSAGE&lt;/code&gt; — text message&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ATTACHMENT&lt;/code&gt; — file attachment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EVENT&lt;/code&gt; — participant join/leave events&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. Signed URL Retrieval
&lt;/h3&gt;

&lt;p&gt;For each attachment with &lt;code&gt;Status: APPROVED&lt;/code&gt;, the handler looks up the customer's phone number and the system phone number from DynamoDB using the &lt;code&gt;contactId&lt;/code&gt;, then retrieves a temporary download URL:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_signed_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;participant_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;AttachmentId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ConnectionToken&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;connectionToken&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Send to WhatsApp
&lt;/h3&gt;

&lt;p&gt;The handler maps the MIME type to the appropriate WhatsApp message type and sends the file using the signed URL as the media link — no need to re-upload the file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_whatsapp_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attachment_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phone_number_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_file_category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mime_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# image, video, audio, or document
&lt;/span&gt;    &lt;span class="n"&gt;message_object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messaging_product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;whatsapp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;recipient_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;individual&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;+&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;message_object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;link&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;attachment_url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;document&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;message_object&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;message_type&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;filename&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;

    &lt;span class="n"&gt;socialessaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_whatsapp_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;originationPhoneNumberId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;phone_number_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;metaApiVersion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;meta_api_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message_object&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MIME prefix&lt;/th&gt;
&lt;th&gt;WhatsApp type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;video/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;video&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;audio/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audio&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;everything else&lt;/td&gt;
&lt;td&gt;&lt;code&gt;document&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For &lt;code&gt;document&lt;/code&gt; types, the original filename is preserved so the customer sees a meaningful file name in their WhatsApp chat.&lt;/p&gt;
&lt;h2&gt;
  
  
  Special Case. Processing Voice Notes
&lt;/h2&gt;

&lt;p&gt;Beyond simple file relay, attachments can be processed to enable advanced use cases. The most compelling example in this solution is voice note handling — converting audio formats and transcribing speech to text.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Problem with Voice Notes
&lt;/h3&gt;

&lt;p&gt;WhatsApp voice notes arrive in OGG/Opus format. Amazon Connect Chat &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/feature-limits.html" rel="noopener noreferrer"&gt;does not support OGG files as attachments&lt;/a&gt;. If you try to upload an OGG file, it will be rejected. So you need a conversion step.&lt;/p&gt;
&lt;h3&gt;
  
  
  OGG → WAV Conversion
&lt;/h3&gt;

&lt;p&gt;A dedicated Lambda function (&lt;code&gt;convert_to_wav&lt;/code&gt;) handles the format conversion using ffmpeg. After conversion, the inbound handler reads the WAV content from S3 and uploads it to Connect Chat as &lt;code&gt;voice.wav&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Real-Time Transcription with Amazon Transcribe Streaming
&lt;/h3&gt;

&lt;p&gt;The original OGG file is also sent to a &lt;code&gt;transcribe_audio&lt;/code&gt; Lambda for speech-to-text conversion. This uses Amazon Transcribe Streaming — not the batch API — for near real-time results.&lt;/p&gt;
&lt;h2&gt;
  
  
  Beyond Voice Notes: Advanced Processing Ideas
&lt;/h2&gt;

&lt;p&gt;The same pattern — intercept, process, forward — can be extended to other attachment types for advanced use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image understanding&lt;/strong&gt;: Use Amazon Bedrock or Amazon Rekognition to analyze photos. A customer sends a photo of a damaged product? Extract a description and attach it to the chat alongside the image. Useful for insurance claims or warranty requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Video analysis&lt;/strong&gt;: Extract key frames from video attachments and run them through multimodal models for understanding. A customer sends a video of a malfunctioning device? Summarize the issue for the agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document extraction&lt;/strong&gt;: Use Amazon Textract or multimodal Foundation Models to extract text from scanned documents, invoices, or forms. Pre-fill case details before the agent even opens the chat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language detection and translation&lt;/strong&gt;: Detect the language of voice notes or text in images and translate them before forwarding to the agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The inbound handler is designed to be extensible — you can add processing steps between the download and the upload to Connect without changing the overall flow.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deployment Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before getting started you'll need:&lt;/p&gt;
&lt;h3&gt;
  
  
  WhatsApp Business Account
&lt;/h3&gt;

&lt;p&gt;To get started, you need to create a new WhatsApp Business Account (WABA) or migrate an existing one to AWS. The main steps are described &lt;a href="https://docs.aws.amazon.com/social-messaging/latest/userguide/getting-started.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;. In summary:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have or create a Meta Business Account&lt;/li&gt;
&lt;li&gt;Access the AWS End User Messaging Social console and link your business account through the embedded Facebook portal&lt;/li&gt;
&lt;li&gt;Make sure you have a phone number that can receive SMS/voice verification and add it to WhatsApp&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⚠️ Important: Do not use your personal WhatsApp number for this.&lt;/p&gt;
&lt;h3&gt;
  
  
  An Amazon Connect Instance
&lt;/h3&gt;

&lt;p&gt;You need an Amazon Connect instance. If you don't have one yet, you can &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;follow this guide&lt;/a&gt; to create one.&lt;/p&gt;

&lt;p&gt;You'll need the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; of your instance. You can find it in the Amazon Connect console or in the instance ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  A Chat Flow to Handle Messages
&lt;/h3&gt;

&lt;p&gt;Create or have ready the contact flow that defines the user experience. &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/create-contact-flow.html" rel="noopener noreferrer"&gt;Follow this guide&lt;/a&gt; to create an Inbound Contact Flow. The simplest one will work.&lt;/p&gt;

&lt;p&gt;Remember to publish the flow.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qp65ezw0uaovvsvss7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qp65ezw0uaovvsvss7o.png" alt="Simple Flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take note of the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; and &lt;strong&gt;CONTACT_FLOW_ID&lt;/strong&gt; from the Details tab. The values are in the flow ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID/contact-flow/CONTACT_FLOW_ID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(see the &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/general_connect_eum.md" rel="noopener noreferrer"&gt;WhatsApp / Connect Prerequisites&lt;/a&gt; for more details)&lt;/p&gt;
&lt;h3&gt;
  
  
  Important: Enable Attachments in the Amazon Connect Instance
&lt;/h3&gt;

&lt;p&gt;Follow &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/enable-attachments.html" rel="noopener noreferrer"&gt;this steps&lt;/a&gt; to enable attachment sharing.&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying with AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Deploy in the same region where your AWS End User Messaging WhatsApp numbers are configured.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Clone the repository and navigate to the project
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-whatsapp-end-user-messaging-connect-chat/whatsapp-eum-connect-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Deploy with CDK
&lt;/h3&gt;

&lt;p&gt;Follow the instructions in the &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;CDK Deployment Guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Post-deployment Configuration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Update the SSM Parameter
&lt;/h3&gt;

&lt;p&gt;After deployment, update the SSM parameter &lt;code&gt;/whatsapp_eum_connect_chat/config&lt;/code&gt; with your Amazon Connect details:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"instance_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-connect-instance-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contact_flow_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-contact-flow-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chat_duration_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_reactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_stickers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instance_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your Amazon Connect Instance ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contact_flow_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The ID of the Inbound Contact Flow for chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chat_duration_minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;How long the chat session stays active (default: 60)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ignore_reactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whether to ignore WhatsApp reactions (default: yes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ignore_stickers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whether to ignore WhatsApp stickers (default: yes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Step 2: Add the Event Destination
&lt;/h3&gt;

&lt;p&gt;After deploying the stack, use the created SNS topic as your event destination in the AWS End User Messaging Social console.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to AWS Systems Manager Parameter Store and copy the value of &lt;code&gt;/whatsapp_eum_connect_chat/topic/in&lt;/code&gt; (it starts with &lt;code&gt;arn:aws:sns&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkuyintfmpqjwt9hgmii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkuyintfmpqjwt9hgmii.png" alt="Topic Parameter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the AWS End User Messaging Social console, select destination &lt;strong&gt;Amazon SNS&lt;/strong&gt; and paste the &lt;strong&gt;Topic ARN&lt;/strong&gt; from the previous step 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87sv4kmtsfe863octr3n.png" alt="SNS EUM Configuration"&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Step 3: Configure Transcription Language (Optional)
&lt;/h3&gt;

&lt;p&gt;The transcription language is set to &lt;code&gt;es-US&lt;/code&gt; (Spanish) by default. To change it, edit the &lt;code&gt;language_code&lt;/code&gt; parameter in &lt;code&gt;lambdas/code/transcribe_audio/transcribe.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;transcribe_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_stream_transcription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;language_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en-US&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Change to your target language
&lt;/span&gt;    &lt;span class="n"&gt;media_sample_rate_hz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;48000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;media_encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ogg-opus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Go to your Amazon Connect instance and &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/launch-ccp.html" rel="noopener noreferrer"&gt;open the Contact Control Panel (CCP)&lt;/a&gt;. Send a WhatsApp message to the End User Messaging Social number.&lt;/p&gt;





&lt;p&gt;Try these scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Send a photo — it should appear as an image attachment in the agent's chat&lt;/li&gt;
&lt;li&gt;Send a PDF — it should appear as a document attachment&lt;/li&gt;
&lt;li&gt;Send a voice note — it should arrive as a WAV audio file plus a text transcription&lt;/li&gt;
&lt;li&gt;From the agent side, send an image or document — it should appear in the customer's WhatsApp chat&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;This solution handles the core attachment flow. Some ideas to extend it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multimodal Foundation Model for image analysis on inbound photos (e.g., damage assessment for claims) or videos&lt;/li&gt;
&lt;li&gt;Implement support for video inbound attachments&lt;/li&gt;
&lt;li&gt;Support multiple transcription languages with automatic language detection&lt;/li&gt;
&lt;li&gt;Combine with the &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/whatsapp-eum-connect-chat" rel="noopener noreferrer"&gt;Message Buffering&lt;/a&gt; solution to aggregate rapid messages and the &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/agent-initiated-whatsapp" rel="noopener noreferrer"&gt;Agent-Initiated WhatsApp&lt;/a&gt; solution for full proactive communication&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat" rel="noopener noreferrer"&gt;Project Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Amazon Connect Administrator Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/feature-limits.html" rel="noopener noreferrer"&gt;Amazon Connect Chat — Supported Attachment Types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect-participant/latest/APIReference/API_StartAttachmentUpload.html" rel="noopener noreferrer"&gt;Amazon Connect Participant API — StartAttachmentUpload&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/social-messaging/latest/APIReference/API_SendWhatsAppMessage.html" rel="noopener noreferrer"&gt;AWS End User Messaging Social — SendWhatsAppMessage API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/social-messaging/latest/userguide/what-is-service.html" rel="noopener noreferrer"&gt;AWS End User Messaging Social User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/awslabs/amazon-transcribe-streaming-sdk" rel="noopener noreferrer"&gt;Amazon Transcribe Streaming SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media" rel="noopener noreferrer"&gt;WhatsApp Business Platform — Media Messages&lt;/a&gt;


&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;




&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>whatsapp</category>
      <category>amazonconnect</category>
      <category>attachments</category>
      <category>cdk</category>
    </item>
    <item>
      <title>El hábito más frecuente de los usuarios de WhatsApp (y como se puede manejar)</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Fri, 27 Feb 2026 03:29:10 +0000</pubDate>
      <link>https://dev.to/ensamblador/el-habito-mas-frecuente-de-los-usuarios-de-whatsapp-y-como-se-puede-manejar-1ena</link>
      <guid>https://dev.to/ensamblador/el-habito-mas-frecuente-de-los-usuarios-de-whatsapp-y-como-se-puede-manejar-1ena</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;




&lt;blockquote&gt;
&lt;p&gt;Aprende a optimizar las integraciones de WhatsApp con Amazon Connect mediante el buffering de mensajes rápidos consecutivos en un solo mensaje coherente. Esta guía paso a paso cubre la arquitectura completa usando AWS CDK, DynamoDB Streams, AWS Lambda, AWS End User Messaging Social y Amazon Connect. Reduce costos y ofrece una experiencia de conversación natural para tus agentes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcj6gnb8aatjrsm1j1o6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcj6gnb8aatjrsm1j1o6.gif" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cuando los clientes se comunican por WhatsApp, rara vez envían un solo mensaje. Escriben rápido: "Hola", "Necesito ayuda", "con mi pedido", "P12345". Cada uno de esos mensajes genera un evento de webhook separado y, sin ninguna optimización, cada uno crea un mensaje de chat independiente en Amazon Connect. ¿El resultado? Una conversación fragmentada, un agente confundido y costos innecesarios.&lt;/p&gt;

&lt;p&gt;En los chats con IA, normalmente evitamos que los usuarios envíen mensajes adicionales mientras el agente aún está procesando. Lamentablemente, con mensajes asincrónicos y programáticos, no podemos controlar eso. Pero sí podemos controlar cuánto tiempo esperamos antes de empezar a responder. Este blog no solo aplica para escenarios de WhatsApp a Connect, sino para cualquier canal de chat que enfrente el mismo desafío: SMS, mensajes directos en redes sociales y más.&lt;/p&gt;

&lt;p&gt;En este blog, aprenderás a resolver esto con una capa de buffering de mensajes que agrega mensajes rápidos consecutivos de WhatsApp en un solo mensaje coherente antes de reenviarlos a Amazon Connect.&lt;/p&gt;

&lt;p&gt;Consulta el código en &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat" rel="noopener noreferrer"&gt;https://github.com/aws-samples&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lo que vas a construir
&lt;/h2&gt;

&lt;p&gt;Una capa de buffering entre AWS End User Messaging Social y Amazon Connect que:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Captura los mensajes entrantes de WhatsApp en una tabla de DynamoDB&lt;/li&gt;
&lt;li&gt;Usa DynamoDB Streams con una ventana de agregación (tumbling window) para almacenar mensajes en buffer&lt;/li&gt;
&lt;li&gt;Agrega mensajes de texto consecutivos del mismo remitente en uno solo&lt;/li&gt;
&lt;li&gt;Reenvía el mensaje combinado a Amazon Connect como un único mensaje de chat&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El resultado final: los agentes ven una conversación limpia y natural en lugar de una avalancha de mensajes fragmentados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arquitectura
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1j2id6zrf0hy3sws5hq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1j2id6zrf0hy3sws5hq.png" alt="Architecture Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Así funciona el flujo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Un mensaje de WhatsApp llega a través de AWS End User Messaging Social y se publica en un tema de SNS&lt;/li&gt;
&lt;li&gt;Una función Lambda (&lt;code&gt;on_raw_messages&lt;/code&gt;) almacena cada mensaje en una tabla de DynamoDB (&lt;code&gt;raw_messages&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;DynamoDB Streams activa una función Lambda (&lt;code&gt;message_aggregator&lt;/code&gt;) usando una ventana de agregación como buffer&lt;/li&gt;
&lt;li&gt;El agregador agrupa, ordena y concatena los mensajes de texto del mismo remitente&lt;/li&gt;
&lt;li&gt;El mensaje agregado se reenvía al manejador de eventos de WhatsApp, que crea o actualiza la sesión de chat en Amazon Connect&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  El Problema
&lt;/h2&gt;

&lt;p&gt;Cuando los usuarios envían múltiples mensajes en rápida sucesión:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mensaje 1: Hola
Mensaje 2: Necesito ayuda
Mensaje 3: con mi pedido
Mensaje 4: P12345
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Sin buffering, cada mensaje crea un mensaje de chat separado en Amazon Connect. Esto genera:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Una conversación fragmentada difícil de seguir para los agentes&lt;/li&gt;
&lt;li&gt;Costos más altos (cada mensaje se factura individualmente)&lt;/li&gt;
&lt;li&gt;Múltiples invocaciones de Lambda aguas abajo&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  La Solución: Cómo Funciona el Buffering
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Almacenamiento de Mensajes raw
&lt;/h3&gt;

&lt;p&gt;Los mensajes entrantes de WhatsApp se almacenan en una tabla de DynamoDB (&lt;code&gt;raw_messages&lt;/code&gt;) con:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clave de partición: &lt;code&gt;from&lt;/code&gt; (número de teléfono del remitente)&lt;/li&gt;
&lt;li&gt;Clave de ordenamiento: &lt;code&gt;id&lt;/code&gt; (ID del mensaje)&lt;/li&gt;
&lt;li&gt;TTL habilitado para limpieza automática&lt;/li&gt;
&lt;li&gt;DynamoDB Streams habilitado con ventana de agregación&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Usar &lt;code&gt;from&lt;/code&gt; como clave de partición e &lt;code&gt;id&lt;/code&gt; como clave de ordenamiento garantiza que los mensajes del mismo usuario se almacenen juntos y caigan en el mismo &lt;strong&gt;shard&lt;/strong&gt;, por lo que se procesan secuencialmente por el stream.&lt;/p&gt;

&lt;p&gt;La Lambda function que maneja esto es sencilla:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;sns_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;parse_float&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;decimal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;webhook_entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sns_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;whatsAppWebhookEntry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}))&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;webhook_entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;changes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metadata&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;contacts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contacts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
                &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metadata&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;
                &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messaging_product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messaging_product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="c1"&gt;# Almacenar en DynamoDB
&lt;/span&gt;                &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Procesamiento del Stream con Ventana de Agregación
&lt;/h3&gt;

&lt;p&gt;La ventana de agregación (tumbling window) es la clave de la estrategia de buffering. DynamoDB Streams activa la Lambda &lt;code&gt;message_aggregator&lt;/code&gt;, pero en lugar de invocarla por cada registro individual, la ventana de agregación espera un número configurable de segundos (por defecto: 20) antes de invocar la función con todos los registros acumulados en esa ventana.&lt;/p&gt;

&lt;p&gt;Cada shard invoca una sola Lambda, por lo que los mensajes del mismo usuario dentro de esa ventana se procesan todos juntos.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Lógica de Agregación
&lt;/h3&gt;

&lt;p&gt;El agregador agrupa los mensajes por remitente, los ordena por marca de tiempo y concatena los mensajes de texto consecutivos con saltos de línea. Los mensajes que no son de texto (imágenes, audio, documentos, etc.) se preservan como elementos separados.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Entrada:
  Mensaje 1: Hola
  Mensaje 2: Necesito ayuda
  Mensaje 3: con mi pedido
  Mensaje 4: P12345

Salida (mensaje agregado único):
  Hola
  Necesito ayuda
  con mi pedido
  P12345
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;La lógica principal de agregación:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;raw_records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

    &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;raw_records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eventName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;new_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NewImage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;deserialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deserialize_dynamodb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deserialized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})}&lt;/span&gt;

    &lt;span class="n"&gt;aggregated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;aggregate_all_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Reenviar cada mensaje agregado al manejador de eventos de WhatsApp
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;agg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;aggregated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lambda_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;WHATSAPP_EVENT_HANDLER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;InvocationType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Event&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  4. Reenvío a Amazon Connect
&lt;/h3&gt;

&lt;p&gt;Una vez agregados, la Lambda invoca asincrónicamente la función Lambda WhatsApp Inbound, que crea o actualiza la sesión de chat en Amazon Connect con el mensaje combinado. El agente ve un mensaje limpio en lugar de cuatro mensajes separados.&lt;/p&gt;
&lt;h3&gt;
  
  
  Beneficios
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flujo de conversación natural&lt;/strong&gt;: Múltiples mensajes rápidos aparecen como un solo mensaje coherente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimización de costos&lt;/strong&gt;: Menos mensajes aguas abajo significa menores costos de chat en Amazon Connect&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limpieza automática&lt;/strong&gt;: El TTL elimina los mensajes en crudo automáticamente&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escalable&lt;/strong&gt;: DynamoDB Streams maneja alto rendimiento (hasta 10,000 registros por stream)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confiable&lt;/strong&gt;: El procesamiento del stream asegura que no se pierdan mensajes (entrega al menos una vez)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Estimación de Costos
&lt;/h2&gt;

&lt;p&gt;Escenario de ejemplo: 1,000 mensajes raw agregados en 250 mensajes (suponiendo una proporción de 4:1). Los mensajes son respondidos por un agente humano.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Componente&lt;/th&gt;
&lt;th&gt;Sin Buffering&lt;/th&gt;
&lt;th&gt;Con Buffering&lt;/th&gt;
&lt;th&gt;Ahorro&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB + Streams&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;~$0.0013&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda (todas las funciones)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;~$0.00078&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infraestructura de Buffering&lt;/td&gt;
&lt;td&gt;$0.00&lt;/td&gt;
&lt;td&gt;~$0.002&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Llamadas API entrantes&lt;/td&gt;
&lt;td&gt;1,000 llamadas&lt;/td&gt;
&lt;td&gt;250 llamadas&lt;/td&gt;
&lt;td&gt;75% menos llamadas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Costo Chat Connect (entrada)&lt;/td&gt;
&lt;td&gt;$4.00&lt;/td&gt;
&lt;td&gt;$1.00&lt;/td&gt;
&lt;td&gt;$3.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$4.00&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$1.00&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$3.00 (75%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ten en cuenta que el costo total de la comunicación considera Connect entrada y salida, así como EUM entrada y salida. Aquí solo estamos reduciendo los mensajes de entrada de Amazon Connect Chat.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Costo de Connect Chat: $0.004 × msg (entrada) + $0.004 × msg (salida). &lt;a href="https://aws.amazon.com/connect/pricing/" rel="noopener noreferrer"&gt;Ver precios&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Costo de EUM: $0.005 × msg (entrada) + $0.005 × msg (salida). &lt;a href="https://aws.amazon.com/end-user-messaging/pricing/" rel="noopener noreferrer"&gt;Ver precios&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Prerrequisitos
&lt;/h2&gt;

&lt;p&gt;Antes de comenzar necesitarás:&lt;/p&gt;
&lt;h3&gt;
  
  
  Cuenta de WhatsApp Business
&lt;/h3&gt;

&lt;p&gt;Para empezar, necesitas crear una nueva cuenta de WhatsApp Business (WABA) o migrar una existente a AWS. Los pasos principales se describen &lt;a href="https://docs.aws.amazon.com/social-messaging/latest/userguide/getting-started.html" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;. En resumen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tener o crear una cuenta de Meta Business&lt;/li&gt;
&lt;li&gt;Acceder a la consola de AWS End User Messaging Social y vincular tu cuenta de negocio a través del portal integrado de Facebook&lt;/li&gt;
&lt;li&gt;Asegurarte de tener un número de teléfono que pueda recibir verificación por SMS/voz y agregarlo a WhatsApp&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⚠️ Importante: No uses tu número personal de WhatsApp para esto.&lt;/p&gt;
&lt;h3&gt;
  
  
  Una Instancia de Amazon Connect
&lt;/h3&gt;

&lt;p&gt;Necesitas una instancia de Amazon Connect. Si aún no tienes una, puedes &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;seguir esta guía&lt;/a&gt; para crear una.&lt;/p&gt;

&lt;p&gt;Necesitarás el &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; de tu instancia. Puedes encontrarlo en la consola de Amazon Connect o en el ARN de la instancia:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Un Flujo de Chat para Manejar Mensajes
&lt;/h3&gt;

&lt;p&gt;Crea o ten listo el flujo de contacto que define la experiencia del usuario. &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/create-contact-flow.html" rel="noopener noreferrer"&gt;Sigue esta guía&lt;/a&gt; para crear un flujo de contacto de entrada (Inbound Contact Flow). El más sencillo funcionará.&lt;/p&gt;

&lt;p&gt;Recuerda publicar el flujo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qp65ezw0uaovvsvss7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qp65ezw0uaovvsvss7o.png" alt="Flujo Simple"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Toma nota del &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; y &lt;strong&gt;CONTACT_FLOW_ID&lt;/strong&gt; desde la pestaña de Detalles. Los valores están en el ARN del flujo:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID/contact-flow/CONTACT_FLOW_ID&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Despliegue con AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Despliega en la misma región donde están configurados tus números de WhatsApp en AWS End User Messaging.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Clona el repositorio y navega al proyecto
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-whatsapp-end-user-messaging-connect-chat/whatsapp-eum-connect-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Despliega con CDK
&lt;/h3&gt;

&lt;p&gt;Sigue las instrucciones en la &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;Guía de Despliegue con CDK&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuración Post-despliegue
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Paso 1: Actualizar el Parámetro SSM
&lt;/h3&gt;

&lt;p&gt;Después del despliegue, actualiza el parámetro SSM &lt;code&gt;/whatsapp_eum_connect_chat/config&lt;/code&gt; con los detalles de tu Amazon Connect:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"instance_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;tu-connect-instance-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contact_flow_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;tu-contact-flow-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chat_duration_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_reactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_stickers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parámetro&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instance_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;El ID de tu instancia de Amazon Connect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contact_flow_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;El ID del flujo de contacto de entrada para chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chat_duration_minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cuánto tiempo permanece activa la sesión de chat (por defecto: 60)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ignore_reactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Si se ignoran las reacciones de WhatsApp (por defecto: yes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ignore_stickers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Si se ignoran los stickers de WhatsApp (por defecto: yes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Paso 2: Agregar el Destino de Eventos
&lt;/h3&gt;

&lt;p&gt;Después de desplegar el stack, usa el tema SNS creado como destino de eventos en la consola de AWS End User Messaging Social.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ve a AWS Systems Manager Parameter Store y copia el valor de &lt;code&gt;/whatsapp_eum_connect_chat/topic/in&lt;/code&gt; (comienza con &lt;code&gt;arn:aws:sns&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkuyintfmpqjwt9hgmii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkuyintfmpqjwt9hgmii.png" alt="Parámetro del Tema"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;En la consola de AWS End User Messaging Social, selecciona el destino &lt;strong&gt;Amazon SNS&lt;/strong&gt; y pega el &lt;strong&gt;Topic ARN&lt;/strong&gt; del paso anterior&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87sv4kmtsfe863octr3n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87sv4kmtsfe863octr3n.png" alt="Configuración SNS EUM"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Paso 3: Ajustar el Tiempo de Buffer (Opcional)
&lt;/h3&gt;

&lt;p&gt;La ventana de buffer por defecto es de 20 segundos. Si deseas cambiarla, edita &lt;code&gt;BUFFER_IN_SECONDS&lt;/code&gt; en &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/whatsapp-eum-connect-chat/config.py" rel="noopener noreferrer"&gt;&lt;code&gt;config.py&lt;/code&gt;&lt;/a&gt; y vuelve a desplegar:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;BUFFER_IN_SECONDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;  &lt;span class="c1"&gt;# Cambia este valor (en segundos)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Pruebas
&lt;/h2&gt;

&lt;p&gt;Ve a tu instancia de Amazon Connect y &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/launch-ccp.html" rel="noopener noreferrer"&gt;abre el Panel de Control de Contactos (CCP)&lt;/a&gt;. Envía un mensaje de WhatsApp al número de End User Messaging Social.&lt;/p&gt;

&lt;p&gt;Intenta enviar una secuencia rápida de mensajes y observa cómo llegan como un solo mensaje agregado en Amazon Connect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Próximos Pasos
&lt;/h2&gt;

&lt;p&gt;Aunque este blog se enfoca en WhatsApp y Amazon Connect, el patrón de buffering aplica de forma amplia. Cualquier canal de chat donde los usuarios envíen mensajes rápidos y asincrónicos — SMS, mensajes directos en redes sociales o integraciones personalizadas — puede beneficiarse del mismo enfoque. Si no puedes evitar que los usuarios envíen múltiples mensajes mientras se procesa una respuesta, al menos puedes controlar cuánto tiempo esperas antes de actuar.&lt;/p&gt;

&lt;p&gt;Algunas ideas para extender esta solución:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ajustar la ventana de buffer según tu caso de uso (más corta para tiempo real, más larga para ahorro de costos)&lt;/li&gt;
&lt;li&gt;Agregar una Cola de Mensajes Fallidos (Dead Letter Queue) para el procesamiento fallido del stream&lt;/li&gt;
&lt;li&gt;Implementar lógica de agregación personalizada para tipos de mensajes específicos (por ejemplo, agrupar imágenes)&lt;/li&gt;
&lt;li&gt;Combinar con la solución de &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/agent-initiated-whatsapp" rel="noopener noreferrer"&gt;WhatsApp Iniciado por el Agente&lt;/a&gt; para comunicación bidireccional completa&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Recursos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat" rel="noopener noreferrer"&gt;Repositorio del Proyecto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Guía del Administrador de Amazon Connect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/APIReference/Welcome.html" rel="noopener noreferrer"&gt;Referencia de API de Amazon Connect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/social-messaging/latest/userguide/what-is-service.html" rel="noopener noreferrer"&gt;Guía del Usuario de AWS End User Messaging Social&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html" rel="noopener noreferrer"&gt;Guía del Desarrollador de DynamoDB Streams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html" rel="noopener noreferrer"&gt;DynamoDB Streams y Ventanas de Agregación con Lambda&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;





</description>
      <category>amazonconnect</category>
      <category>whatsapp</category>
      <category>buffering</category>
      <category>cdk</category>
    </item>
    <item>
      <title>The Most Frequent Habit of WhatsApp Users (And How to Handle It)</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Fri, 27 Feb 2026 03:28:52 +0000</pubDate>
      <link>https://dev.to/ensamblador/the-most-frequent-habit-of-whatsapp-users-and-how-to-handle-it-4ogl</link>
      <guid>https://dev.to/ensamblador/the-most-frequent-habit-of-whatsapp-users-and-how-to-handle-it-4ogl</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;




&lt;blockquote&gt;
&lt;p&gt;Learn how to optimize WhatsApp-to-Amazon Connect integrations by buffering rapid consecutive messages into a single coherent message. This step-by-step guide covers the full architecture using AWS CDK, DynamoDB Streams, AWS Lambda, AWS End User Messaging Social, and Amazon Connect.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcj6gnb8aatjrsm1j1o6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcj6gnb8aatjrsm1j1o6.gif" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When customers reach out via WhatsApp, they rarely send a single message. They type fast: "Hello", "I need help", "with my order", "P12345". Each of those messages triggers a separate webhook event, and without any optimization, each one creates a separate chat message in Amazon Connect. The result? A fragmented conversation, a confused agent, and unnecessary costs.&lt;/p&gt;

&lt;p&gt;In AI-powered chats, we usually prevent users from sending additional messages while the agent is still processing. Unfortunately, with asynchronous and programmatic messages, we can't control that. But we can control how long we wait before starting to answer. This blog not only helps with WhatsApp-to-Connect scenarios but applies to every chat channel that faces the same challenge — SMS, social media DMs, and more.&lt;/p&gt;

&lt;p&gt;In this blog, you'll learn how to solve that with a message buffering layer that aggregates rapid consecutive WhatsApp messages into a single, coherent message before forwarding them to Amazon Connect.&lt;/p&gt;

&lt;p&gt;Check out the code at &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat" rel="noopener noreferrer"&gt;https://github.com/aws-samples&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll build
&lt;/h2&gt;

&lt;p&gt;A buffering layer between AWS End User Messaging Social and Amazon Connect that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Captures incoming WhatsApp messages in a DynamoDB table&lt;/li&gt;
&lt;li&gt;Uses DynamoDB Streams with a tumbling window to buffer messages&lt;/li&gt;
&lt;li&gt;Aggregates consecutive text messages from the same sender into one&lt;/li&gt;
&lt;li&gt;Forwards the combined message to Amazon Connect as a single chat message&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The end result: agents see a clean, natural conversation instead of a flood of fragmented messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1j2id6zrf0hy3sws5hq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1j2id6zrf0hy3sws5hq.png" alt="Architecture Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how it flows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A WhatsApp message arrives via AWS End User Messaging Social and is published to an SNS topic&lt;/li&gt;
&lt;li&gt;A Lambda function (&lt;code&gt;on_raw_messages&lt;/code&gt;) stores each message in a DynamoDB table (&lt;code&gt;raw_messages&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;DynamoDB Streams triggers a Lambda function (&lt;code&gt;message_aggregator&lt;/code&gt;) using a tumbling window as a buffer&lt;/li&gt;
&lt;li&gt;The aggregator groups, sorts, and concatenates text messages from the same sender&lt;/li&gt;
&lt;li&gt;The aggregated message is forwarded to the WhatsApp event handler, which creates or updates the Amazon Connect chat session&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;When users send multiple messages in quick succession:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Message 1: Hello
Message 2: I need help
Message 3: with my order
Message 4: P12345
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Without buffering, each message creates a separate Amazon Connect chat message. This leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A fragmented conversation that's hard for agents to follow&lt;/li&gt;
&lt;li&gt;Higher costs (each message is billed individually)&lt;/li&gt;
&lt;li&gt;Multiple Lambda invocations downstream&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How Buffering Works
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Raw Message Storage
&lt;/h3&gt;

&lt;p&gt;Incoming WhatsApp messages are stored in a DynamoDB table (&lt;code&gt;raw_messages&lt;/code&gt;) with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Partition key: &lt;code&gt;from&lt;/code&gt; (sender phone number)&lt;/li&gt;
&lt;li&gt;Sort key: &lt;code&gt;id&lt;/code&gt; (message ID)&lt;/li&gt;
&lt;li&gt;TTL enabled for automatic cleanup&lt;/li&gt;
&lt;li&gt;DynamoDB Streams enabled with a tumbling window&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using &lt;code&gt;from&lt;/code&gt; as the partition key and &lt;code&gt;id&lt;/code&gt; as the sort key guarantees that messages from the same user are stored together and fall into the same shard, so they are processed sequentially by the stream.&lt;/p&gt;

&lt;p&gt;The Lambda that handles this is straightforward:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sns&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
        &lt;span class="n"&gt;sns_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;parse_float&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;decimal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;webhook_entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sns_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;whatsAppWebhookEntry&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}))&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;webhook_entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;changes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metadata&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;contacts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contacts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
                &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;metadata&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;
                &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messaging_product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messaging_product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="c1"&gt;# Store in DynamoDB
&lt;/span&gt;                &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Stream Processing with Tumbling Window
&lt;/h3&gt;

&lt;p&gt;The tumbling window is the key to the buffering strategy. DynamoDB Streams triggers the &lt;code&gt;message_aggregator&lt;/code&gt; Lambda, but instead of invoking it for every single record, the tumbling window waits a configurable number of seconds (default: 20) before invoking the function with all accumulated records in that window.&lt;/p&gt;

&lt;p&gt;Each shard invokes one Lambda, so messages from the same user within that window are processed altogether.&lt;/p&gt;

&lt;p&gt;Look &lt;a href="https://aws.amazon.com/es/blogs/database/build-scalable-event-driven-architectures-with-amazon-dynamodb-and-aws-lambda/" rel="noopener noreferrer"&gt;here&lt;/a&gt; if you want to dive deep.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Aggregation Logic
&lt;/h3&gt;

&lt;p&gt;The aggregator groups messages by sender, sorts them by timestamp, and concatenates consecutive text messages with newlines. Non-text messages (images, audio, documents, etc.) are preserved as separate items.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input:
  Message 1: Hello
  Message 2: I need help
  Message 3: with my order
  Message 4: P12345

Output (single aggregated message):
  Hello
  I need help
  with my order
  P12345
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The core aggregation logic:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;raw_records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

    &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;raw_records&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;eventName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;new_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dynamodb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NewImage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
            &lt;span class="n"&gt;deserialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deserialize_dynamodb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deserialized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})}&lt;/span&gt;

    &lt;span class="n"&gt;aggregated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;aggregate_all_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;records&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Forward each aggregated message to the WhatsApp event handler
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;agg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;aggregated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lambda_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;FunctionName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;WHATSAPP_EVENT_HANDLER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;InvocationType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Event&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;state&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  4. Forwarding to Amazon Connect
&lt;/h3&gt;

&lt;p&gt;Once aggregated, the Lambda asynchronously invokes the WhatsApp event handler, which creates or updates the Amazon Connect chat session with the combined message. The agent sees one clean message instead of four separate ones.&lt;/p&gt;
&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Natural conversation flow&lt;/strong&gt;: Multiple rapid messages appear as one coherent message&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost optimization&lt;/strong&gt;: Fewer messages downstream means lower Amazon Connect chat costs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic cleanup&lt;/strong&gt;: TTL removes old raw messages automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable&lt;/strong&gt;: DynamoDB Streams handles high throughput (up to 10,000 records per stream)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliable&lt;/strong&gt;: Stream processing ensures no messages are lost (at-least-once delivery)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Cost Estimation
&lt;/h2&gt;

&lt;p&gt;Example scenario: 1,000 raw messages aggregated into 250 messages (4:1 ratio assumption). Messages are answered by a human agent.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Without Buffering&lt;/th&gt;
&lt;th&gt;With Buffering&lt;/th&gt;
&lt;th&gt;Savings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB + Streams&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;~$0.0013&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda (all functions)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;~$0.00078&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Buffering Infrastructure&lt;/td&gt;
&lt;td&gt;$0.00&lt;/td&gt;
&lt;td&gt;~$0.002&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inbound API Calls&lt;/td&gt;
&lt;td&gt;1,000 calls&lt;/td&gt;
&lt;td&gt;250 calls&lt;/td&gt;
&lt;td&gt;75% fewer calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connect Chat (In) Cost&lt;/td&gt;
&lt;td&gt;$4.00&lt;/td&gt;
&lt;td&gt;$1.00&lt;/td&gt;
&lt;td&gt;$3.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$4.00&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$1.00&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$3.00 (75%)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note the whole cost considers connect in and out as well EUM in and out. Here we are only reducing Amazon Connect Chat in messages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Connect Chat cost: $0.004 × msg (in) + $0.004 × msg (out). &lt;a href="https://aws.amazon.com/connect/pricing/" rel="noopener noreferrer"&gt;See pricing&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;EUM cost: $0.005 × msg (in) + $0.005 × msg (out). &lt;a href="https://aws.amazon.com/end-user-messaging/pricing/" rel="noopener noreferrer"&gt;See pricing&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before getting started you'll need:&lt;/p&gt;
&lt;h3&gt;
  
  
  WhatsApp Business Account
&lt;/h3&gt;

&lt;p&gt;To get started, you need to create a new WhatsApp Business Account (WABA) or migrate an existing one to AWS. The main steps are described &lt;a href="https://docs.aws.amazon.com/social-messaging/latest/userguide/getting-started.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;. In summary:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have or create a Meta Business Account&lt;/li&gt;
&lt;li&gt;Access the AWS End User Messaging Social console and link your business account through the embedded Facebook portal&lt;/li&gt;
&lt;li&gt;Make sure you have a phone number that can receive SMS/voice verification and add it to WhatsApp&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⚠️ Important: Do not use your personal WhatsApp number for this.&lt;/p&gt;
&lt;h3&gt;
  
  
  An Amazon Connect Instance
&lt;/h3&gt;

&lt;p&gt;You need an Amazon Connect instance. If you don't have one yet, you can &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;follow this guide&lt;/a&gt; to create one.&lt;/p&gt;

&lt;p&gt;You'll need the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; of your instance. You can find it in the Amazon Connect console or in the instance ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  A Chat Flow to Handle Messages
&lt;/h3&gt;

&lt;p&gt;Create or have ready the contact flow that defines the user experience. &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/create-contact-flow.html" rel="noopener noreferrer"&gt;Follow this guide&lt;/a&gt; to create an Inbound Contact Flow. The simplest one will work.&lt;/p&gt;

&lt;p&gt;Remember to publish the flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qp65ezw0uaovvsvss7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qp65ezw0uaovvsvss7o.png" alt="Simple Flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take note of the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; and &lt;strong&gt;CONTACT_FLOW_ID&lt;/strong&gt; from the Details tab. The values are in the flow ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID/contact-flow/CONTACT_FLOW_ID&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying with AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Deploy in the same region where your AWS End User Messaging WhatsApp numbers are configured.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Clone the repository and navigate to the project
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-whatsapp-end-user-messaging-connect-chat/whatsapp-eum-connect-chat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Deploy with CDK
&lt;/h3&gt;

&lt;p&gt;Follow the instructions in the &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;CDK Deployment Guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Post-deployment Configuration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Update the SSM Parameter
&lt;/h3&gt;

&lt;p&gt;After deployment, update the SSM parameter &lt;code&gt;/whatsapp_eum_connect_chat/config&lt;/code&gt; with your Amazon Connect details:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"instance_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-connect-instance-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"contact_flow_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-contact-flow-id&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"chat_duration_minutes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_reactions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignore_stickers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yes"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;instance_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your Amazon Connect Instance ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contact_flow_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The ID of the Inbound Contact Flow for chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chat_duration_minutes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;How long the chat session stays active (default: 60)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ignore_reactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whether to ignore WhatsApp reactions (default: yes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ignore_stickers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whether to ignore WhatsApp stickers (default: yes)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Step 2: Add the Event Destination
&lt;/h3&gt;

&lt;p&gt;After deploying the stack, use the created SNS topic as your event destination in the AWS End User Messaging Social console.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to AWS Systems Manager Parameter Store and copy the value of &lt;code&gt;/whatsapp_eum_connect_chat/topic/in&lt;/code&gt; (it starts with &lt;code&gt;arn:aws:sns&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkuyintfmpqjwt9hgmii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkuyintfmpqjwt9hgmii.png" alt="Topic Parameter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the AWS End User Messaging Social console, select destination &lt;strong&gt;Amazon SNS&lt;/strong&gt; and paste the &lt;strong&gt;Topic ARN&lt;/strong&gt; from the previous step&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87sv4kmtsfe863octr3n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87sv4kmtsfe863octr3n.png" alt="SNS EUM Configuration"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Adjust the Buffer Time (Optional)
&lt;/h3&gt;

&lt;p&gt;The default buffer window is 20 seconds. If you want to change it, edit &lt;code&gt;BUFFER_IN_SECONDS&lt;/code&gt; in &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/whatsapp-eum-connect-chat/config.py" rel="noopener noreferrer"&gt;&lt;code&gt;config.py&lt;/code&gt;&lt;/a&gt; and redeploy:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;BUFFER_IN_SECONDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;  &lt;span class="c1"&gt;# Change this value (in seconds)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Go to your Amazon Connect instance and &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/launch-ccp.html" rel="noopener noreferrer"&gt;open the Contact Control Panel (CCP)&lt;/a&gt;. Send a WhatsApp message to the End User Messaging Social number.&lt;/p&gt;

&lt;p&gt;Try sending a rapid sequence of messages and see how they arrive as a single aggregated message in Amazon Connect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;This solution is a starting point. Some ideas to extend it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adjust the buffer window based on your use case (shorter for real-time, longer for cost savings)&lt;/li&gt;
&lt;li&gt;Add a Dead Letter Queue for failed stream processing&lt;/li&gt;
&lt;li&gt;Implement custom aggregation logic for specific message types (e.g., group images together)&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Combine with the &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/tree/main/agent-initiated-whatsapp" rel="noopener noreferrer"&gt;Agent-Initiated WhatsApp&lt;/a&gt; solution for full bidirectional communication&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat" rel="noopener noreferrer"&gt;Project Repository&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Amazon Connect Administrator Guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/APIReference/Welcome.html" rel="noopener noreferrer"&gt;Amazon Connect API Reference&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/social-messaging/latest/userguide/what-is-service.html" rel="noopener noreferrer"&gt;AWS End User Messaging Social User Guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html" rel="noopener noreferrer"&gt;DynamoDB Streams Developer Guide&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://aws.amazon.com/es/blogs/database/build-scalable-event-driven-architectures-with-amazon-dynamodb-and-aws-lambda/" rel="noopener noreferrer"&gt;Build scalable, event-driven architectures with Amazon DynamoDB and AWS Lambda&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;





</description>
      <category>amazonconnect</category>
      <category>whatsapp</category>
      <category>buffer</category>
      <category>cdk</category>
    </item>
    <item>
      <title>Agent-Initiated WhatsApp Messages from Amazon Connect</title>
      <dc:creator>ensamblador</dc:creator>
      <pubDate>Thu, 26 Feb 2026 02:46:05 +0000</pubDate>
      <link>https://dev.to/ensamblador/agent-initiated-whatsapp-messages-from-amazon-connect-33gg</link>
      <guid>https://dev.to/ensamblador/agent-initiated-whatsapp-messages-from-amazon-connect-33gg</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;




&lt;blockquote&gt;
&lt;p&gt;Learn how to build a proactive WhatsApp messaging experience from Amazon Connect, enabling customer service agents to send WhatsApp template messages with a single click from the Agent Workspace. This step-by-step guide covers the full architecture using AWS CDK, AWS Lambda, AWS End User Messaging Social, and Amazon Connect, including the configuration of Contact Flows, custom forms (Views), and Lambda functions to retrieve customer data and send messages. Ideal for teams looking to automate and simplify outbound WhatsApp communication without leaving the Amazon Connect console.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhv7pstb4x9rkmbphe9o.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhv7pstb4x9rkmbphe9o.gif" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Does your customer service team need to send proactive WhatsApp messages? Imagine an agent being able to send a WhatsApp template message to a customer with a single click, right from their Amazon Connect desktop. No switching apps, no copy-pasting phone numbers, no mistakes.&lt;/p&gt;

&lt;p&gt;In this blog I'll show you how to build that experience using AWS CDK, AWS Lambda, AWS End User Messaging Social, and Amazon Connect.&lt;/p&gt;

&lt;p&gt;Check out the code at &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat" rel="noopener noreferrer"&gt;https://github.com/aws-samples&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What are we building?
&lt;/h2&gt;

&lt;p&gt;A guided experience inside the Amazon Connect Agent Workspace that allows agents to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;See customer data pre-loaded in a form&lt;/li&gt;
&lt;li&gt;Review and edit WhatsApp template parameters&lt;/li&gt;
&lt;li&gt;Send the message with a single click&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All orchestrated by a Contact Flow, Lambda functions, and a custom form (Connect view).&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvz9pveewz089x85iodl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdvz9pveewz089x85iodl.png" alt="Architecture Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how it flows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Contact Flow invokes a Lambda function (&lt;code&gt;get_customer_data&lt;/code&gt;) to retrieve customer information (name, phone number, etc.). You can also use the &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/customer-profiles-block.html" rel="noopener noreferrer"&gt;&lt;code&gt;Customer Profile&lt;/code&gt;&lt;/a&gt; block to look up customer information in Connect Customer Profiles.&lt;/li&gt;
&lt;li&gt;The flow presents a form (View) in the Agent Workspace with pre-filled data&lt;/li&gt;
&lt;li&gt;The agent reviews, edits if needed, and submits&lt;/li&gt;
&lt;li&gt;A second Lambda function (&lt;code&gt;send_whatsapp_message&lt;/code&gt;) sends the WhatsApp template message via the AWS End User Messaging Social API&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before getting started you'll need:&lt;/p&gt;

&lt;h3&gt;
  
  
  WhatsApp Business Account
&lt;/h3&gt;

&lt;p&gt;To get started, you need to create a new WhatsApp Business Account (WABA) or migrate an existing one to AWS. The main steps are described &lt;a href="https://docs.aws.amazon.com/social-messaging/latest/userguide/getting-started.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;. In summary:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have or create a Meta Business account&lt;/li&gt;
&lt;li&gt;Access the AWS End User Messaging Social console and link your business account through the embedded Facebook portal&lt;/li&gt;
&lt;li&gt;Make sure you have a phone number that can receive SMS/voice verification and add it to WhatsApp&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⚠️ Important: Do not use your personal WhatsApp number for this.&lt;/p&gt;

&lt;h3&gt;
  
  
  An Amazon Connect Instance
&lt;/h3&gt;

&lt;p&gt;You need an Amazon Connect instance. If you don't have one yet, you can &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/amazon-connect-instances.html" rel="noopener noreferrer"&gt;follow this guide&lt;/a&gt; to create one.&lt;/p&gt;

&lt;p&gt;You'll need the &lt;strong&gt;INSTANCE_ID&lt;/strong&gt; of your instance. You can find it in the Amazon Connect console or in the instance ARN:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;arn:aws:connect:&amp;lt;region&amp;gt;:&amp;lt;account_id&amp;gt;:instance/INSTANCE_ID&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;(see the &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/general_connect_eum.md" rel="noopener noreferrer"&gt;WhatsApp / Connect Prerequisites&lt;/a&gt; for more details)&lt;/p&gt;

&lt;h3&gt;
  
  
  WhatsApp Message Template Creation
&lt;/h3&gt;

&lt;p&gt;A WhatsApp message template created in End User Messaging (see the &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/general_template_creation.md" rel="noopener noreferrer"&gt;Template Creation Guide&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying with AWS CDK
&lt;/h2&gt;

&lt;p&gt;⚠️ Deploy in the same region where your AWS End User Messaging WhatsApp numbers are configured.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Clone the repository and navigate to the project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sample-whatsapp-end-user-messaging-connect-chat/agent-initiated-whatsapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Set the Instance ID
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;config.py&lt;/code&gt; and set your Amazon Connect Instance ID:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;INSTANCE_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your-connect-instance-id&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Deploy with CDK
&lt;/h3&gt;

&lt;p&gt;Follow the instructions in the &lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat/blob/main/general_cdk_deploy.md" rel="noopener noreferrer"&gt;CDK Deployment Guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Post-deployment configuration
&lt;/h2&gt;

&lt;p&gt;Once deployed, there are a few manual steps to wire everything together.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Update the SSM Parameter
&lt;/h3&gt;

&lt;p&gt;Go to AWS Systems Manager Parameter Store and update &lt;code&gt;/whatsapp_template/config&lt;/code&gt; with your template configuration:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"messaging_product"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"whatsapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PHONE_NUMBER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"recipient_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"individual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your_template_name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en_US"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"components"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"META_API_VERSION"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"v23.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ORIGINATION_PHONE_NUMBER_ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-origination-phone-number-id&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;template.name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of your WhatsApp template created in End User Messaging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;template.language.code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Template language code (e.g.: &lt;code&gt;en_US&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ORIGINATION_PHONE_NUMBER_ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The phone number ID in AWS End User Messaging Social&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;META_API_VERSION&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Meta API version (default: &lt;code&gt;v23.0&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Step 2: Explore the deployed form
&lt;/h3&gt;

&lt;p&gt;Navigate to the views in your Amazon Connect instance:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;your-instance&amp;gt;.my.connect.aws/views
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Look for the view named &lt;code&gt;enviarWhatsAppForm007&lt;/code&gt;. This is the form that agents will use to review customer data and send the message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6526lwbouuunttdpbw28.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6526lwbouuunttdpbw28.png" alt="Deployed Form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: the WhatsApp field is of type password to hide the number. It could even be removed from the form entirely and accessed using &lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/connect-contact-attributes.html" rel="noopener noreferrer"&gt;Contact Attributes&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Configure the deployed Contact Flow
&lt;/h3&gt;

&lt;p&gt;Navigate to the contact flow &lt;code&gt;SendWhatsAppGuideFlow007&lt;/code&gt; in your Amazon Connect console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff7ngwig5uds7jqqdvwer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff7ngwig5uds7jqqdvwer.png" alt="Contact Flow"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  3.1 Configure the first Lambda (Get customer data)
&lt;/h4&gt;

&lt;p&gt;Edit the first &lt;strong&gt;Invoke AWS Lambda function&lt;/strong&gt; block and select the pre-deployed Lambda function. Look for the one containing &lt;code&gt;GetCustomerData&lt;/code&gt; in the name.&lt;/p&gt;

&lt;p&gt;This is a mock function that returns sample data:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fullName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John Doe&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;phoneNumber&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;+XXXXXXXX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Delivered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Puzzle 1000 pieces&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;P12345&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Replace this with your own data source. For example, you could do a data dip to Amazon Connect Customer Profiles using a &lt;code&gt;profileId&lt;/code&gt;, query a DynamoDB table, or call an external API.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Confirm&lt;/strong&gt; to save.&lt;/p&gt;
&lt;h4&gt;
  
  
  3.2 Configure the Show View block
&lt;/h4&gt;

&lt;p&gt;Edit the &lt;strong&gt;Show view&lt;/strong&gt; block and select &lt;code&gt;enviarWhatsAppForm007&lt;/code&gt; as the view resource.&lt;/p&gt;

&lt;p&gt;The form default values are mapped from the values returned by the Lambda:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;fullName&lt;/code&gt; → &lt;code&gt;$.External.fullName&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;whatsappNumber&lt;/code&gt; → &lt;code&gt;$.External.phoneNumber&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;input1&lt;/code&gt; to &lt;code&gt;input4&lt;/code&gt; → &lt;code&gt;$.External.input1&lt;/code&gt; to &lt;code&gt;$.External.input4&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These values pre-fill the form so the agent can review them before sending. Click &lt;strong&gt;Confirm&lt;/strong&gt; to save.&lt;/p&gt;
&lt;h4&gt;
  
  
  3.3 Configure the second Lambda (Send WhatsApp message)
&lt;/h4&gt;

&lt;p&gt;Edit the second &lt;strong&gt;Invoke AWS Lambda function&lt;/strong&gt; block and select the Lambda containing &lt;code&gt;SendWhatsappMessage&lt;/code&gt; in the ARN.&lt;/p&gt;

&lt;p&gt;This Lambda reads the template configuration from SSM, extracts the form values from the contact attributes, and sends the message:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Load the template configuration from SSM Parameter Store
&lt;/span&gt;    &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_ssm_parameter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CONFIG_PARAM_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message_payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Extract form values from contact attributes
&lt;/span&gt;    &lt;span class="n"&gt;attributes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Details&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ContactData&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Attributes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;phone_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;phoneNumber&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Build template parameters from input1..input4
&lt;/span&gt;    &lt;span class="n"&gt;template_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_template_parameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Send using AWS End User Messaging Social
&lt;/span&gt;    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;social_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_whatsapp_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;originationPhoneNumberId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;origination_phone_number_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message_payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;metaApiVersion&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;meta_api_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OK&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messageId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messageId&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Click &lt;strong&gt;Confirm&lt;/strong&gt; to save. Then &lt;strong&gt;Save&lt;/strong&gt; and &lt;strong&gt;Publish&lt;/strong&gt; the contact flow.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 4: Create a new View (Guide)
&lt;/h3&gt;

&lt;p&gt;Navigate to the Amazon Connect views:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;your-instance&amp;gt;.my.connect.aws/views
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Create a new view of type &lt;strong&gt;Guide&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1x94lf2f0feio05oqs5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft1x94lf2f0feio05oqs5.png" alt="Create View"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  4.1 Add a Connect Application component
&lt;/h4&gt;

&lt;p&gt;Drag a &lt;strong&gt;Connect Application&lt;/strong&gt; component onto the canvas.&lt;/p&gt;
&lt;h4&gt;
  
  
  4.2 Configure the component
&lt;/h4&gt;

&lt;p&gt;Set the &lt;code&gt;contactFlowId&lt;/code&gt; to the deployed contact flow &lt;code&gt;SendWhatsAppGuideFlow007&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3bl2i4aqpr94ui9yzop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3bl2i4aqpr94ui9yzop.png" alt="Create Guide"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  4.3 Name and publish
&lt;/h4&gt;

&lt;p&gt;Give the view a name and click &lt;strong&gt;Publish&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 5: Create a custom Workspace
&lt;/h3&gt;
&lt;h4&gt;
  
  
  5.1 Create the workspace
&lt;/h4&gt;

&lt;p&gt;Navigate to the Amazon Connect workspaces:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;your-instance&amp;gt;.my.connect.aws/workspaces
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Click &lt;strong&gt;Add new workspace&lt;/strong&gt; and fill in the name, description, and title.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7dorzked230f1cgjfjrt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7dorzked230f1cgjfjrt.png" alt="Create Workspace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assign this workspace to the users or routing profiles you need.&lt;/p&gt;
&lt;h4&gt;
  
  
  5.2 Add a page with the guide
&lt;/h4&gt;

&lt;p&gt;Add a new page using &lt;strong&gt;Set page with custom page slug&lt;/strong&gt; and select the view you created in the previous step (the one with the Connect Application component).&lt;/p&gt;

&lt;p&gt;Use a custom slug like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/page/send_whatsapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnu6ptxi9oiogjmw81mu3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnu6ptxi9oiogjmw81mu3.png" alt="Add Page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save the page.&lt;/p&gt;
&lt;h4&gt;
  
  
  5.3 Navigate to the custom workspace
&lt;/h4&gt;

&lt;p&gt;Select your custom workspace from the top navigation bar in the Agent Workspace.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyx2j6ux6bnp6jo6n8yvy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyx2j6ux6bnp6jo6n8yvy.png" alt="Select Workspace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The agent can now navigate to the custom page and use the guide to send WhatsApp template messages to customers.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;When the agent navigates to the custom page, they are presented with a Connect Application that runs the contact flow. The flow displays the form with pre-loaded values from the customer data Lambda, giving the agent the opportunity to review, modify, and submit. Once the agent submits, the WhatsApp sending Lambda is invoked with all the form parameters to deliver the final message.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;This solution is a starting point. Some ideas to extend it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect the customer data Lambda to Amazon Connect Customer Profiles or DynamoDB&lt;/li&gt;
&lt;li&gt;Add additional validations to the form&lt;/li&gt;
&lt;li&gt;Support multiple WhatsApp templates selectable by the agent&lt;/li&gt;
&lt;li&gt;Dynamically incorporate the number of inputs, depending on the number of variables in the template&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws-samples/sample-whatsapp-end-user-messaging-connect-chat" rel="noopener noreferrer"&gt;Project Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/adminguide/what-is-amazon-connect.html" rel="noopener noreferrer"&gt;Amazon Connect Administrator Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/connect/latest/APIReference/Welcome.html" rel="noopener noreferrer"&gt;Amazon Connect API Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/social-messaging/latest/APIReference/API_SendWhatsAppMessage.html" rel="noopener noreferrer"&gt;AWS End User Messaging Social - SendWhatsAppMessage API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/social-messaging/latest/userguide/what-is-service.html" rel="noopener noreferrer"&gt;AWS End User Messaging Social User Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates" rel="noopener noreferrer"&gt;WhatsApp Business Platform - Template Message Structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.facebook.com/docs/whatsapp/cloud-api/reference/messages#template-object" rel="noopener noreferrer"&gt;WhatsApp Business Platform - Template Components Reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;Let's Build!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Autor:&lt;/strong&gt;&lt;br&gt;


&lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__242047"&gt;
    &lt;a href="/ensamblador" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F242047%2F4e391627-8eb1-4aa4-99ff-43f99f1296e8.jpg" alt="ensamblador image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ensamblador"&gt;ensamblador&lt;/a&gt;Follow
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ensamblador"&gt;AWS Specialist Solutions Architect
Applied AI @ AWS
Opinions expressed are solely my own and do not express the views or opinions of my employer.&lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;





</description>
      <category>amazonconnect</category>
      <category>whatsapp</category>
      <category>cdk</category>
      <category>python</category>
    </item>
  </channel>
</rss>
