Módulo 6: Canales reales: chat web, WhatsApp, Telegram y voz
4. Bots de Telegram
Descripción
Al terminar esta lección vas a poder exponer el mismo sistema de agentes como un bot de Telegram completo: crear el bot y obtener su token hablando con otro bot, configurar el Telegram Trigger con los eventos correctos, leer el update entrante y sacarle la identidad del usuario, responder con el nodo Telegram, y —lo más valioso de este canal— montar botones debajo de los mensajes con el patrón de dos triggers que gobierna cualquier interfaz conversacional con botones, incluida la de WhatsApp.
Esto importa por dos razones que van en direcciones distintas. La primera es práctica: Telegram es gratis, no cobra por mensaje, no te pide verificar un negocio, no tiene ventana de 24 horas, y un bot se crea en dos minutos sin llenar un solo formulario. Es el mejor laboratorio del módulo, y si tu verificación en Meta sigue en trámite, es donde vas a practicar todo lo de la lección anterior sin esperar a nadie. La segunda es que Telegram no es solo un simulador: es un canal real donde hay comunidades, soporte técnico y herramientas internas de equipos completos, y saber montar un bot con un agente detrás es una habilidad que se pide sola.
Conexión con el módulo: vienes de WhatsApp, donde todo lo estructural estaba enterrado bajo trámites de Meta. Aquí vas a reencontrar exactamente las mismas piezas —trigger, filtro, normalización, cerebro, respuesta— pero sin fricción, y con el tiempo mental libre para aprender lo que WhatsApp no te dejó practicar cómodo: los botones interactivos y el ciclo de Callback Query. La lección 6 va a generalizar esos botones a los cuatro canales, y la 7 va a convertir la normalización que hagas aquí en un contrato formal. El cerebro del Módulo 5 sigue sin tocarse.
El bot que se crea hablando con un bot
Piensa en la diferencia entre pedir una línea telefónica de empresa y comprar una tarjeta SIM en una tienda de conveniencia. La primera es un contrato: documentos, verificación, días de espera, una factura mensual. La segunda es un intercambio de treinta segundos en un mostrador: te dan un número y ya puedes llamar.
Telegram es la tarjeta SIM. Y el mostrador es, curiosamente, otro bot: para crear un bot en Telegram le escribes a BotFather, que es el bot oficial que administra bots. Le mandas un comando, te pide un nombre, te devuelve un token, y con eso ya tienes un canal funcionando. No hay panel de desarrollador, no hay portafolio de negocio, no hay aprobación.
Esa facilidad tiene una consecuencia pedagógica que vale la pena aprovechar a propósito: en Telegram puedes equivocarte gratis. Cuando estés diseñando cómo debe verse una conversación con botones, o probando qué pasa si el agente tarda quince segundos, o midiendo cuánto texto es demasiado, hazlo aquí primero. Cada iteración cuesta cero y no consume la cuota de nada.
Crear el bot
Tres pasos, y de verdad son tres.
Paso 1 — Habla con BotFather. Abre Telegram —en el teléfono o en la versión de escritorio, da igual—, busca @BotFather y empieza una conversación. Es una cuenta verificada de Telegram; asegúrate de que tenga la marca de verificación, porque hay imitaciones.
Paso 2 — Crea el bot. Mándale el comando /newbot. Te va a pedir dos cosas en orden:
- Un nombre para mostrar. Es lo que la gente ve arriba del chat. Puede tener espacios y acentos:
Asistente TuTienda. - Un nombre de usuario. Es el identificador único, tiene que terminar en
boty no puede estar tomado:tutienda_support_bot.
Qué esperar. BotFather responde con un mensaje de felicitación que incluye el token, con esta forma:
8123456789:AAH_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
│ │
│ └── la parte secreta
└── el ID numérico de tu bot
Ese token es la contraseña completa del bot. Quien lo tenga puede mandar mensajes como tu bot, leer todo lo que le escriban y borrarlo. No lo pegues en un chat, no lo subas a un repositorio, no lo dejes en una captura de pantalla. Si se te escapa, /revoke en BotFather genera uno nuevo y anula el viejo — está bien, se arregla en diez segundos, pero conviene no llegar ahí.
Paso 3 — Guárdalo en n8n. En n8n, crea una credencial de tipo Telegram API y pega el token. Eso es todo: una sola credencial, que sirve tanto para recibir como para enviar. Compara con WhatsApp, que necesitaba dos.
Perfecto. Ya tienes el canal. Literalmente en dos minutos.
Antes de seguir, un par de comandos de BotFather que vale la pena conocer porque afectan la experiencia y nadie los menciona:
/setdescription— el texto que se ve antes de que alguien escriba por primera vez. Es el equivalente delInitial Messagede la lección 2 y sirve exactamente para lo mismo: acotar qué puede hacer el agente antes de que alguien pregunte lo que no puede./setcommands— registra la lista de comandos (/start,/ayuda,/pedido) que Telegram muestra en un menú al lado del cuadro de texto. Es un descubrimiento de funciones prácticamente gratuito./setprivacy— decide si el bot ve todos los mensajes de un grupo o solo los que lo mencionan. Por defecto está en modo privado, que es lo correcto casi siempre y que causa un error muy específico, tratado más abajo.
El Telegram Trigger
El nodo Telegram Trigger es el que escucha. Tiene un parámetro principal —la lista de updates a los que se suscribe— y un puñado de opciones.
La lista completa de updates que expone el nodo es larga, y la mayoría no te va a hacer falta nunca:
* (todos, con tres excepciones) Business Connection Business Message
Callback Query ◄── botones Channel Post Chat Boost
Chat Join Request Chat Member Chosen Inline Result
Deleted Business Messages Edited Business Message Edited Channel Post
Edited Message Inline Query Message ◄── lo básico
Message Reaction Message Reaction Count My Chat Member
Poll Poll Answer Pre-Checkout Query
Purchased Paid Media Removed Chat Boost Shipping Query
Para un agente conversacional necesitas dos, y solo dos:
- Message — alguien escribió algo.
- Callback Query — alguien tocó un botón de los que tu bot puso debajo de un mensaje.
Fíjate en el valor *: significa "todos los updates excepto tres". No lo uses. Suscribirte a todo te llena el registro de ejecuciones con reacciones con emoji, cambios de miembros y encuestas, cada uno disparando el agente con un payload que no entiende. Es el mismo problema de los eventos de estado de WhatsApp, con otro traje.
Las opciones del nodo que sí valen la pena:
- Download Images/Files. Si está activo, cuando alguien manda una foto o un archivo, n8n lo descarga y lo entrega como dato binario en la salida del trigger. Sin esto, solo recibes un identificador de archivo y hay que descargarlo aparte con la operación
File → Get File. Con Image Size eliges la resolución cuando hay varias. - Restrict to Chat IDs y Restrict to User IDs. Listas separadas por coma. Solo se procesan los updates de esos chats o de esas personas. Esto es oro puro mientras desarrollas: pon tu propio ID de usuario y tu bot queda efectivamente privado aunque cualquiera pueda encontrarlo. Es la forma más simple de tener un canal público que solo te responde a ti.
Anatomía del update entrante
Cuando alguien le escribe a tu bot, el trigger entrega un objeto con esta forma. Como siempre: confirma la ruta exacta contra una ejecución real de tu versión antes de escribir expresiones, porque n8n a veces desanida parte del objeto.
{
"update_id": 908070605,
"message": {
"message_id": 41,
"from": {
"id": 987654321,
"is_bot": false,
"first_name": "Ana",
"last_name": "Ramírez",
"username": "ana_r",
"language_code": "es"
},
"chat": {
"id": 987654321,
"first_name": "Ana",
"type": "private"
},
"date": 1753200000,
"text": "Hola, ¿cómo va mi pedido #4521?"
}
}
Cuatro campos hacen el trabajo, y uno de ellos merece un párrafo entero.
message.text— lo que escribió. Solo existe si el mensaje es de texto; una foto traephoto, un audio traevoice, un sticker traesticker. Misma trampa que en WhatsApp.message.from.id— el identificador de la persona. Es único y estable.message.chat.id— el identificador de la conversación. En un chat privado coincide con el anterior. En un grupo, no:chat.ides el grupo (y es negativo) mientrasfrom.idsigue siendo la persona.message.from.first_nameyusername— para saludar. Elusernamepuede no existir, porque en Telegram es opcional; una expresión que lo asuma va a fallar con quien no lo tenga configurado.
La distinción entre chat.id y from.id parece pedante y no lo es: decide a qué se le llama una conversación en tu sistema. Si usas chat.id como clave de memoria y el bot está en un grupo, todo el grupo comparte un solo historial —que puede ser exactamente lo que quieres para un bot de equipo—. Si usas from.id, cada persona tiene el suyo aunque escriban en el mismo grupo. Para un bot de atención al cliente en chats privados los dos valores son iguales y da lo mismo; el día que alguien agregue el bot a un grupo, deja de dar lo mismo.
Y hay un detalle práctico que sorprende: para responderle a alguien necesitas su chat.id, y solo lo tienes si esa persona le escribió primero a tu bot. Un bot de Telegram no puede iniciar una conversación con un desconocido. No es una limitación de tiempo como la ventana de WhatsApp; es más absoluta: sin un primer mensaje de la persona, no hay a quién escribirle. Es, en el fondo, la misma filosofía —el usuario decide cuándo se abre la puerta— implementada de otra forma.
Ejemplo trabajado: el agente del Módulo 5 como bot
Mismo cerebro, misma estructura que en WhatsApp, mucho menos ruido.
Telegram Trigger (updates: Message)
│
├─► IF: ¿trae texto?
│
├─► Set: normalize_incoming ← el adaptador de entrada
│
├─► Telegram → Send Chat Action ← "escribiendo…" (opcional, muy recomendable)
│
├─► AI Agent: triage_agent ← el cerebro, intacto
│ ├─ Postgres Chat Memory (Session ID = telegram:<from.id>)
│ ├─ AI Agent Tool: order_specialist
│ └─ AI Agent Tool: billing_specialist
│
└─► Telegram → Send Message ← el adaptador de salida
Paso 1 — El filtro. Igual que en WhatsApp, descartar lo que no es texto:
# Nodo: IF — Name: is_text_message
# Un sticker, un audio o una foto no traen message.text.
Condición: {{ $json.message.text }} → existe / no vacío
Paso 2 — El adaptador de entrada. Los mismos cuatro campos que en WhatsApp, con otros orígenes. Fíjate en que los nombres de los campos son idénticos — eso no es casualidad y es la semilla de la lección 7:
# Nodo: Set — Name: normalize_incoming
# Mismo formato de salida que el adaptador de WhatsApp.
# Lo único que cambia es de dónde sale cada valor.
channel = "telegram"
channel_user_id = {{ $json.message.from.id }}
customer_name = {{ $json.message.from.first_name }}
text = {{ $json.message.text }}
Paso 3 — El indicador de "escribiendo". Este paso es opcional y es la mejora de percepción más barata del módulo entero. El nodo Telegram tiene una operación Send Chat Action que muestra en la pantalla del usuario el clásico "escribiendo…" debajo del nombre del bot:
# Nodo: Telegram — Name: show_typing
# Resource: Chat · Operation: Send Chat Action
Chat ID: {{ $('normalize_incoming').item.json.channel_user_id }}
Action: typing
Dura unos cinco segundos o hasta que llegue el mensaje real, lo que ocurra primero. Si tu agente tarda más que eso, puedes repetirlo. Sin este nodo, el triage_agent delegando en dos especialistas produce entre ocho y quince segundos de silencio absoluto, y en un chat quince segundos de silencio son una eternidad — mucha gente escribe otra vez, lo cual dispara otra ejecución y empeora todo.
Guarda ese nodo en la memoria: es la solución de Telegram al mismo problema que en la lección 2 resolvimos con Response Mode: Using Response Nodes. Cada canal tiene su forma de decir "estoy pensando", y no tenerla es de los errores de UX que más se notan.
Paso 4 — La memoria. Aquí aparece una decisión que en WhatsApp no se veía, porque allá había un solo identificador natural:
# Nodo: Postgres Chat Memory (conectado al triage_agent)
Session ID: Define below
Key: telegram:{{ $('normalize_incoming').item.json.channel_user_id }}
Nota el prefijo telegram:. No es decoración. Los identificadores de Telegram son números y los de WhatsApp también; sin un prefijo, un usuario de Telegram con ID 5215512345678 compartiría historial con el teléfono de WhatsApp 5215512345678. La probabilidad es baja, la consecuencia es un cliente leyendo la conversación de otro, y el prefijo cuesta ocho caracteres. Ponlo siempre. La lección 7 formaliza esta idea como clave de sesión compuesta.
Paso 5 — El agente. Como en WhatsApp, hay que decirle de dónde leer:
# Nodo: AI Agent — Name: triage_agent
Source for Prompt (User Message): Define below
Prompt (User Message): {{ $json.text }}
Paso 6 — La respuesta.
# Nodo: Telegram — Name: send_telegram_reply
# Resource: Message · Operation: Send Message
Chat ID: {{ $('normalize_incoming').item.json.channel_user_id }}
Text: {{ $json.output }}
# Additional Fields (verifica las etiquetas en tu versión):
# Parse Mode: dejar VACÍO por ahora — ver la advertencia de abajo
Qué esperar. Guarda, activa el workflow, busca tu bot en Telegram por su nombre de usuario y escríbele. Vas a ver el "escribiendo…" y unos segundos después la respuesta del triage_agent. En la pestaña de ejecuciones está la traza completa con sus delegaciones. Exactamente lo mismo que en WhatsApp y en la web, con un cerebro que no se enteró de nada.
El formato: por qué Parse Mode está vacío
Telegram no interpreta ningún formato por defecto. Si el agente escribe **#4521**, la persona ve literalmente los asteriscos. Para que haya negritas hay que activar Parse Mode, que tiene tres valores: Markdown (una versión vieja y limitada), MarkdownV2 (la actual) y HTML.
Y aquí está la trampa, que es real y muerde el primer día en producción: MarkdownV2 exige escapar un montón de caracteres, y si aparece uno sin escapar, Telegram rechaza el mensaje entero con un error de la API. Los caracteres problemáticos incluyen el guion bajo, el asterisco, los corchetes, los paréntesis, el guion, el punto y el signo de admiración. Piensa en la respuesta más normal del mundo:
El pedido #4521 llega el 23/07/2026. ¡Gracias por tu compra!
Esos puntos y ese signo de admiración bastan para que el mensaje falle. Y el texto lo escribe un modelo de lenguaje, así que no puedes garantizar qué caracteres va a producir. Es una combinación desagradable: formato opcional, escapado obligatorio, y contenido impredecible.
Tres estrategias, en orden de sensatez para un agente:
- No usar
Parse Modeen absoluto. El texto sale plano y nada falla nunca. Para un agente de atención al cliente esto es perfectamente aceptable: casi nadie echa de menos las negritas en un chat de soporte. Es la opción por defecto que recomienda esta lección. - Usar
HTML. Es bastante más tolerante: solo hay que escapar<,>y&, y solo acepta un puñado de etiquetas (<b>,<i>,<code>,<a>). Si de verdad quieres negritas, esta es la vía menos frágil. - Usar
MarkdownV2y escapar el texto con un nodoCodeantes de enviarlo. Funciona, pero estás escribiendo una función de escapado para una necesidad estética. Rara vez vale la pena.
Hay una cuarta cosa que no hay que hacer, y es la tentación inmediata: pedirle en el system prompt al agente que no use caracteres especiales. Eso es meter una regla de canal dentro del cerebro —el error conceptual que la lección 1 marcó— y además no funciona de forma fiable, porque un modelo va a escribir un punto final tarde o temprano. El formato es responsabilidad del adaptador de salida.
Ah, y el otro límite del canal: 4096 caracteres por mensaje. Un agente que redacta una explicación larga puede pasarse, y el mensaje falla entero. La lección 6 trata cómo partir mensajes largos; por ahora basta con saber que el límite existe y que es la segunda causa de mensajes que no llegan.
Botones: el patrón de dos triggers
Esta es la parte que hace que valga la pena aprender Telegram aunque tu canal final sea WhatsApp. Los botones son la mejor herramienta de UX conversacional que existe, y su mecánica es idéntica en los dos canales.
Un mensaje con botones se manda agregándole un teclado en línea (inline keyboard), que es una estructura de filas y botones que aparece pegada debajo del texto. Cada botón lleva un texto visible y un dato oculto —el callback_data— que es lo que tu workflow recibe cuando alguien lo toca.
En el nodo Telegram, operación Send Message, esto vive bajo Reply Markup → Inline Keyboard. La estructura conceptual es esta:
# Nodo: Telegram — Name: ask_which_order
# Resource: Message · Operation: Send Message
# Reply Markup: Inline Keyboard
Text: Tienes dos pedidos abiertos. ¿Cuál quieres consultar?
Inline Keyboard:
Fila 1:
- Text: "Pedido #4521 — en camino" Callback Data: "order:4521"
Fila 2:
- Text: "Pedido #4498 — entregado" Callback Data: "order:4498"
Fila 3:
- Text: "Ninguno, es otra cosa" Callback Data: "order:none"
Los nombres exactos de los campos del constructor de teclados varían entre versiones de n8n; abre el nodo y mira las etiquetas antes de dar por buena la forma de arriba.
Ahora, lo importante: cuando alguien toca un botón, ese evento NO llega al Telegram Trigger como un mensaje. Llega como un update de tipo Callback Query, que es un tipo distinto y trae otro payload:
{
"update_id": 908070606,
"callback_query": {
"id": "4382018475849234",
"from": { "id": 987654321, "first_name": "Ana" },
"message": { "message_id": 41, "chat": { "id": 987654321 } },
"data": "order:4521"
}
}
Ese data es exactamente el callback_data que pusiste en el botón. Y el flujo completo tiene esta forma de dos ramas:
Telegram Trigger (updates: Message, Callback Query)
│
├── ¿el update trae `message`? ─────► rama de texto
│ normalizar → agente → responder
│
└── ¿el update trae `callback_query`? ─────► rama de botón
│
├─► Telegram → Callback → Answer Query ◄── OBLIGATORIO
│ (quita el "relojito" del botón)
│
├─► Set: normalizar, con text = interpretación del data
│ "order:4521" → text = "Consultar el estado del pedido 4521"
│
└─► agente → responder
Dos cosas de ese diagrama merecen atención.
El Answer Query no es opcional. Cuando alguien toca un botón, Telegram muestra un indicador de carga sobre él y lo mantiene hasta que tu bot confirme que recibió el evento. Si nunca confirmas, el botón se queda girando unos segundos y luego la persona ve un aviso de que algo falló — aunque tu agente haya respondido perfectamente. Es un nodo de una línea que evita una sensación de rotura. La operación es Callback → Answer Query y basta con pasarle el id del callback.
El callback_data hay que traducirlo a lenguaje. Tu agente entiende español, no order:4521. La rama del botón tiene que convertir ese dato en una frase que el triage_agent pueda procesar como si el cliente la hubiera escrito. Eso es traducción pura, y por eso vive en el adaptador y no en el cerebro. Y fíjate en la ventaja enorme que esto da: mientras un cliente que escribe "el de los audífonos, creo" obliga al agente a adivinar, un botón entrega un dato exacto. Cada botón es una ambigüedad menos.
Un detalle técnico que ahorra un dolor de cabeza: el callback_data tiene un límite de 64 bytes. No metas ahí un JSON grande ni un texto largo. Mete una clave corta (order:4521) y, si necesitas más contexto, recupéralo del historial o de una tool.
Errores comunes
Suscribirse a * y ahogar el registro (práctico). Qué pasa: alguien deja el trigger en "todos los updates", y a partir de ahí cada reacción con emoji, cada persona que entra o sale de un grupo y cada edición de mensaje dispara el workflow. El agente recibe payloads que no entiende, el registro se llena de ejecuciones fallidas, y encontrar la corrida que de verdad importaba se vuelve un trabajo. Por qué pasa: * parece la opción segura, la que "no se pierde nada". Cómo detectarlo: abre tres ejecuciones al azar; si dos de ellas no traen message ni callback_query, es esto. Cómo corregirlo: suscríbete solo a Message y Callback Query, y agrega otro update el día que tengas una razón concreta para necesitarlo.
El bot en un grupo que no ve los mensajes (práctico). Qué pasa: alguien agrega el bot a un grupo de trabajo, la gente escribe, y el trigger no dispara nunca — salvo cuando alguien menciona al bot por su nombre de usuario. Parece que el bot está roto. Por qué pasa: Telegram tiene un modo de privacidad activo por defecto en el que un bot dentro de un grupo solo recibe los mensajes que lo mencionan explícitamente, los que responden a uno suyo y los comandos. Es una protección de privacidad razonable, y no está documentada en ningún lugar donde uno la busque. Cómo detectarlo: si funciona en chat privado y no en grupo, es esto casi con seguridad. Cómo corregirlo: /setprivacy en BotFather, desactivar el modo privado, y —dato que hace perder media hora— sacar el bot del grupo y volver a agregarlo, porque el cambio no aplica retroactivamente a los grupos donde ya estaba.
Activar MarkdownV2 y ver mensajes que desaparecen (práctico). Qué pasa: se activa el parse mode para que las negritas funcionen, y a partir de ahí algunos mensajes llegan y otros no, sin patrón aparente. El registro de n8n muestra un error de la API de Telegram sobre una entidad que no se pudo analizar. Por qué pasa: MarkdownV2 exige escapar más de una decena de caracteres, incluidos el punto y el guion, que aparecen en cualquier frase normal. Como el texto lo genera un modelo, unos mensajes traen caracteres problemáticos y otros no. Cómo detectarlo: el error de la API menciona can't parse entities o similar, y el mensaje fallido siempre tiene un punto, un guion o un signo de admiración. Cómo corregirlo: quita el Parse Mode —el texto plano nunca falla— o cambia a HTML, que solo exige escapar tres caracteres. No intentes resolverlo pidiéndole al agente que evite ciertos caracteres: es una regla de canal en la capa equivocada y además no es fiable.
Olvidar el Answer Query (práctico). Qué pasa: los botones funcionan, el agente responde bien, y aun así la persona ve el botón girando y después un aviso de error. La experiencia se siente rota aunque todo funcionó. Por qué pasa: Telegram espera una confirmación explícita de que recibiste el evento del botón, y esa confirmación es un nodo aparte que es fácil no saber que existe. Cómo detectarlo: es visual — el indicador de carga sobre el botón que no se va. Cómo corregirlo: un nodo Telegram con Resource: Callback y Operation: Answer Query, lo más pegado posible al trigger en la rama de botón, antes de que el agente empiece a razonar. Ponerlo antes y no después es importante: si el agente tarda diez segundos, el botón estuvo girando diez segundos.
Usar chat.id como clave de memoria sin pensar en grupos (conceptual). Qué pasa: el bot funciona perfecto en chats privados. Alguien lo agrega a un grupo, y de pronto todos los miembros comparten un solo historial: el agente le responde a una persona con el contexto de la conversación de otra. Por qué pasa: en chat privado chat.id y from.id son el mismo número, así que la diferencia es invisible hasta que aparece un grupo. Cómo detectarlo: si chat.id es negativo, estás en un grupo. Cómo corregirlo: decide a propósito qué es una conversación en tu sistema. Para atención al cliente, from.id — el historial es de la persona. Para un asistente de equipo, chat.id — el historial es del grupo, y eso es deseable. Lo que no sirve es no haber elegido.
Ejercicios
Ejercicio 1 — Traduce los botones a lenguaje. El triage_agent detecta que el cliente tiene tres pedidos abiertos y quiere preguntarle cuál consultar. Diseña el mensaje con botones: escribe el texto, los tres botones con su texto visible y su callback_data, y la frase en que la rama de callback convierte cada data antes de pasársela al agente. Después explica por qué esa conversión no puede vivir en el system prompt del agente.
Ver solución
El mensaje:
Text: Veo tres pedidos abiertos a tu nombre. ¿Cuál quieres consultar?
Inline Keyboard:
Fila 1: "#4521 — en camino" → callback_data: "order:4521"
Fila 2: "#4498 — entregado" → callback_data: "order:4498"
Fila 3: "#4470 — en preparación" → callback_data: "order:4470"
Fila 4: "Es sobre otra cosa" → callback_data: "order:none"
La conversión en la rama de callback:
"order:4521" → text = "Consultar el estado del pedido 4521."
"order:4498" → text = "Consultar el estado del pedido 4498."
"order:4470" → text = "Consultar el estado del pedido 4470."
"order:none" → text = "No es sobre un pedido; preguntar de qué se trata."
Por qué no puede vivir en el prompt del agente: el agente nunca ve la cadena order:4521. Lo que recibe es lo que el adaptador le entrega en el campo de prompt. Si le pasaras el callback_data crudo, tendrías que enseñarle en el system prompt a interpretar un formato inventado por ti —lo cual funciona, pero mete conocimiento de la capa de canal dentro del cerebro, y el día que agregues WhatsApp con otro formato de botones tendrías que enseñarle otro—. Traducir en el adaptador mantiene al cerebro hablando un solo idioma: español.
Un detalle de diseño que vale la pena notar: el cuarto botón, "Es sobre otra cosa". Sin él, un cliente cuya pregunta no era sobre ninguno de los tres pedidos se queda sin salida y tiene que escribir texto libre ignorando los botones — cosa que mucha gente no hace, porque los botones se leen como las únicas opciones disponibles. Una salida explícita en cada menú de botones es una regla de UX conversacional que la lección 6 va a repetir.
Por qué funciona: el ejercicio hace visible que un botón es dos cosas a la vez, una etiqueta para la persona y un dato para tu sistema, y que la traducción entre ese dato y el lenguaje del agente es trabajo del adaptador.
Ejercicio 2 — Compara los tres canales. Llena esta tabla para los tres canales que llevas: chat web, WhatsApp y Telegram. Después escribe una línea sobre cuál de las diferencias te parece más consecuente para el diseño de una conversación.
| Chat web | Telegram | ||
|---|---|---|---|
| Identidad que entrega | |||
| ¿Es estable entre sesiones? | |||
| Credenciales que necesita n8n | |||
| ¿Puede el bot escribir primero? | |||
| Límite de longitud | |||
| ¿Botones sin trámite? | |||
| Costo por mensaje |
Ver solución
| Chat web | Telegram | ||
|---|---|---|---|
| Identidad que entrega | sessionId aleatorio por pestaña | número de teléfono | from.id / chat.id |
| ¿Es estable entre sesiones? | No, salvo que la inyectes con metadata | Sí, totalmente | Sí |
| Credenciales que necesita n8n | Ninguna | Dos (OAuth2 + token de API) | Una (token del bot) |
| ¿Puede el bot escribir primero? | No aplica: la persona abre el widget | Solo con plantilla aprobada y pagada | No: hace falta que la persona escriba primero |
| Límite de longitud | Práctico, no duro | 4096 caracteres | 4096 caracteres |
| ¿Botones sin trámite? | Con el nodo Chat y respuestas de aprobación | Soportados por la plataforma; verifica el soporte en tu versión del nodo | Sí, teclado en línea sin restricciones |
| Costo por mensaje | Cero (solo pagas tokens del modelo) | Cero dentro de la ventana de 24 h; plantillas se cobran | Cero |
La diferencia más consecuente para el diseño es si el bot puede escribir primero, y no es la que suele señalarse. Determina si tu producto puede tener flujos proactivos —seguimientos, recordatorios, avisos— o si toda la actividad tiene que ser reactiva. En Telegram, sorprendentemente, la limitación es más dura que en WhatsApp: WhatsApp al menos te vende una forma de iniciar (la plantilla), mientras que en Telegram, si la persona nunca le escribió a tu bot, sencillamente no hay a quién escribirle. Mucha gente asume lo contrario, porque Telegram es más permisivo en todo lo demás.
La segunda diferencia más consecuente es la identidad, y ahí el orden se invierte: WhatsApp te regala la mejor identidad posible sin que hagas nada, mientras que en el chat web tienes que fabricarla.
Por qué funciona: la tabla es el material de la lección 7. Un adaptador por canal existe precisamente porque estas siete filas dan valores distintos.
Ejercicio 3 — Monta el bot y mide el silencio. Con tu bot funcionando, haz esta medición y anota los números: (a) manda una pregunta simple que el triage_agent resuelva con una sola delegación y cronometra desde que envías hasta que llega la respuesta; (b) manda una pregunta con dos temas, que fuerce dos delegaciones, y cronometra igual; (c) repite las dos con el nodo Send Chat Action puesto y sin él, y describe la diferencia en cómo se siente.
Ver solución
No hay números correctos —dependen de tu modelo, tu instancia y tu conexión— pero el patrón que sale casi siempre es este, y lo importante es que lo midas en el tuyo:
- Una delegación: del orden de cinco a diez segundos.
- Dos delegaciones: del orden del doble, entre diez y veinte.
Y la parte que de verdad importa del ejercicio: la diferencia percibida entre tener y no tener el indicador de "escribiendo" es enorme, y no cambia ni un milisegundo del tiempo real. Sin él, los quince segundos se sienten como una conversación rota; con él, se sienten como alguien consultando algo. Es el mejor retorno por nodo agregado de todo el módulo.
Dos observaciones que suelen aparecer al hacer esta medición y vale la pena que aparezcan:
El Send Chat Action dura unos cinco segundos. Si tu peor caso son quince, el indicador se apaga a mitad de camino y el silencio vuelve. La solución más simple es mandar un mensaje de texto real —"Dame un momento, estoy consultando"— en vez de o además del indicador. Cuesta un mensaje más y resuelve el caso largo entero.
La gente escribe otra vez cuando hay silencio. Si en tu prueba te dieron ganas de escribir "¿hola?" a los diez segundos, tus clientes también lo van a hacer, y cada uno de esos mensajes dispara otra ejecución del agente sobre una conversación que ya estaba en curso. Es un problema real de los canales asíncronos y la lección 6 lo trata de frente. Por ahora basta con que lo hayas visto pasar en tu propio bot.
Por qué funciona: es la primera vez en la guía que mides percepción y no corrección. Un agente que responde bien pero se siente roto es un agente que no se usa, y ese criterio no aparece en ninguna traza de ejecución.
Resumen y siguiente paso
Telegram te dio el canal completo en dos minutos y sin pagarle a nadie: un bot creado hablando con BotFather, una sola credencial, un trigger con dos updates —Message y Callback Query— y el mismo cerebro del Módulo 5 respondiendo detrás. Aprendiste a leer el update entrante y a distinguir chat.id de from.id, que es lo que decide qué es una conversación en tu sistema; a prefijar la clave de memoria con el nombre del canal para que dos identificadores numéricos de canales distintos nunca colisionen; a dejar el Parse Mode vacío porque el escapado de MarkdownV2 es incompatible con texto generado por un modelo; y a montar botones con el patrón de dos triggers, con su Answer Query obligatorio y su traducción de callback_data a lenguaje en la capa de adaptación.
Antes de avanzar deberías poder: nombrar los dos updates que necesita un agente conversacional y explicar por qué * es mala idea; decir qué pasa si olvidas el Answer Query y por qué conviene ponerlo antes del agente y no después; y explicar por qué la conversión de order:4521 a una frase en español vive en el adaptador y no en el system prompt.
Lo que sigue es el canal que casi nadie enseña y que el mercado sí está pidiendo. La lección 5 va a voz: qué es realmente un agente de voz por dentro —la pila de reconocimiento, modelo y síntesis—, las dos arquitecturas posibles para conectarlo con n8n y cuál se usa en la práctica, y cómo se registran tus workflows como herramientas de un agente de Vapi, Retell o ElevenLabs. Es también donde el diseño de la conversación cambia más: sin pantalla, sin botones y sin posibilidad de releer, casi todo lo que aprendiste sobre cómo debe responder un agente hay que replantearlo.
Recursos
- Telegram Trigger node — n8n Docs — la lista completa de updates y las opciones de descarga de archivos y restricción por chat o usuario.
- Telegram node — n8n Docs — las operaciones de mensaje, chat, callback y archivo; confirma ahí las etiquetas exactas del constructor de teclados en tu versión.
- Telegram credentials — n8n Docs — dónde pegar el token de BotFather y qué permisos implica.
- Telegram Bot API — Telegram — la fuente de verdad del payload, los límites (4096 caracteres, 64 bytes de
callback_data) y la lista de acciones de chat disponibles. - Formatting options — Telegram Bot API — la lista exacta de caracteres que
MarkdownV2obliga a escapar; vale la pena mirarla una vez para entender por qué esta lección recomienda no usarlo. - From BotFather to Hello World — Telegram — el tutorial oficial de creación de bots, útil para los comandos de configuración (
/setcommands,/setprivacy) que esta lección solo nombró.