BotNetwork

Webhook de un bot de Telegram: setWebhook, token secreto y depuración

En otro artículo comparamos webhooks y polling. Este es la parte práctica: registrar, asegurar y depurar un webhook de Telegram.

Publicado 4 min de lectura

Antes de empezar

Si todavía decides entre los dos modos, lee primero webhooks vs polling. Para un webhook, la Bot API exige:

  • Una URL pública HTTPS. Telegram solo admite los puertos 443, 80, 88 y 8443.
  • Un certificado válido. Uno autofirmado funciona si lo subes con el parámetro certificate; la mayoría de hostings (Railway, Cloudflare Workers, un VPS con Caddy) ya te dan un certificado de confianza.
  • Un handler que responda rápido con HTTP 2xx. Telegram considera falla cualquier otro estado y reintenta.

Mientras haya un webhook configurado, getUpdates no funciona. Usas un modo o el otro.

1. Elige la URL y un secreto

Usa una ruta difícil de adivinar y genera un valor secreto aparte:

export TOKEN="123456:ABC..."            # de @BotFather, fuera de git
export HOOK_URL="https://bot.example.com/telegram/webhook"
export HOOK_SECRET="$(openssl rand -hex 32)"

El secret_token puede tener 1-256 caracteres con A-Z, a-z, 0-9, _ y -. Una cadena hexadecimal de 64 caracteres cabe.

2. Llama a setWebhook

curl -s "https://api.telegram.org/bot$TOKEN/setWebhook" \
  -H "Content-Type: application/json" \
  -d "{
    \"url\": \"$HOOK_URL\",
    \"secret_token\": \"$HOOK_SECRET\",
    \"allowed_updates\": [\"message\", \"callback_query\"],
    \"drop_pending_updates\": true,
    \"max_connections\": 40
  }"

Qué hace cada parámetro:

Parámetro Para qué sirve
url Dónde Telegram hace POST de los updates. Una cadena vacía quita el webhook.
secret_token Se devuelve en el encabezado X-Telegram-Bot-Api-Secret-Token de cada petición.
allowed_updates Solo se entregan estos tipos de update. Si lo omites, se conserva el ajuste anterior.
drop_pending_updates Descarta la cola acumulada mientras el bot estaba caído.
max_connections Conexiones HTTPS simultáneas, 1-100 (40 por defecto). Bájalo en un servidor pequeño.
ip_address IP fija a usar en lugar de resolver DNS.

El constructor de URLs de Telegram arma estas llamadas, y el probador de webhooks sirve para probarlas.

3. Verifica el secreto en tu handler

Quien conozca la URL puede enviar updates falsos. Compara el encabezado con tu secreto y rechaza lo que no coincida antes de hacer cualquier trabajo:

// Cloudflare Worker o cualquier runtime tipo fetch
export default {
  async fetch(req, env) {
    if (req.method !== "POST") return new Response("ok");
    if (req.headers.get("X-Telegram-Bot-Api-Secret-Token") !== env.HOOK_SECRET) {
      return new Response("forbidden", { status: 401 });
    }
    const update = await req.json();
    // pasa el trabajo lento a una cola o waitUntil y responde rápido
    return new Response("ok");
  },
};

Los frameworks lo hacen por ti si les pasas el secreto: webhookCallback(bot, "cloudflare-mod", { secretToken }) en grammY, run_webhook(secret_token=...) en python-telegram-bot y SimpleRequestHandler(secret_token=...) en aiogram.

4. Revisa getWebhookInfo

curl -s "https://api.telegram.org/bot$TOKEN/getWebhookInfo"

Los campos que importan al depurar:

  • url: vacío significa que no hay webhook (o que alguien llamó a deleteWebhook).
  • pending_update_count: si crece, las entregas están fallando.
  • last_error_date y last_error_message: la razón exacta de la última falla.
  • allowed_updates: confirma los tipos que esperas.

Valores comunes de last_error_message y cómo resolverlos:

Mensaje (abreviado) Causa probable
Wrong response from the webhook: 401 / 403 Tu verificación del secreto rechaza a Telegram; no coincide el nombre o el valor del encabezado.
Wrong response from the webhook: 404 La ruta no coincide con la que sirve tu app.
Wrong response from the webhook: 500 / 502 Tu handler falló o la app está caída. Revisa los logs.
SSL error ... / certificate verify failed Cadena de certificados incompleta o certificado autofirmado sin subir.
Connection timed out / Read timeout expired Handler demasiado lento o un firewall bloquea a Telegram.

Para ver exactamente lo que envía Telegram, apunta un bot de prueba a una URL temporal de nuestro inspector de webhooks y revisa encabezados y cuerpo.

5. Responde rápido, trabaja después

Telegram sigue reintentando las entregas fallidas, así que un handler lento puede recibir el mismo update otra vez. Devuelve 200 rápido y pasa el trabajo lento a una cola o tarea en segundo plano. Elimina duplicados por update_id si el procesamiento tiene efectos.

Un atajo útil: puedes responder directamente en el cuerpo de la respuesta del webhook devolviendo JSON con un campo method, por ejemplo {"method":"sendMessage","chat_id":123,"text":"Hola"}. No recibes el resultado de esa llamada, así que úsalo solo para respuestas simples.

Volver a polling

curl -s "https://api.telegram.org/bot$TOKEN/deleteWebhook?drop_pending_updates=true"

Sin deleteWebhook, un bot local que use getUpdates recibe un error 409 Conflict.

Preguntas frecuentes

¿Por qué mi bot recibe el mismo mensaje dos veces?

Tu endpoint devolvió un estado distinto de 2xx o tardó demasiado, y Telegram reintentó. Responde de inmediato y procesa en segundo plano.

¿Puedo correr dos instancias del bot con un webhook?

Sí, detrás de un balanceador, porque cada update es una petición HTTP aparte. Es una ventaja frente al long polling, donde solo puede haber un consumidor de getUpdates.

¿Cuánto guarda Telegram los updates si mi servidor está caído?

Los updates sin procesar se guardan del lado de Telegram hasta entregarse, pero no más de 24 horas, según la documentación de la Bot API.

Fuentes