Skip to content
Integration

Consuming Telegram Bot Webhooks: A Practical Integration Guide

Receive Telegram bot updates over webhooks — setWebhook with a secret_token, X-Telegram-Bot-Api-Secret-Token verification, getWebhookInfo diagnostics, update_id dedupe, and a debugging checklist.

Published 7 min read
On this page

Telegram bots can receive updates two ways: long-polling with getUpdates, or a webhook where Telegram pushes each update to a URL you set. Webhooks are the right choice for anything running as a service — no polling loop, updates arrive as they happen. But Telegram’s webhook model differs from Stripe’s or GitHub’s in one important way: it does not sign updates. Authenticity is your responsibility, established when you register the webhook. This guide covers setting up Telegram webhooks, securing them without a signature, and diagnosing the ones that fail.

What Telegram webhooks are

When you register a webhook with the Bot API method setWebhook, Telegram sends each new Update as an HTTP POST to your URL. An Update is a JSON object with a monotonically increasing update_id and exactly one content field naming what happened — message, callback_query, my_chat_member, and so on. Your bot reacts by calling Bot API methods (sendMessage, answerCallbackQuery) in response.

Common use cases

  • Command and message handling — respond to message updates for chat commands and conversations.
  • Inline keyboards — handle callback_query when a user taps a button.
  • Membership changes — react to my_chat_member when the bot is added to, or removed from, a chat, or its permissions change.
  • Moderation and workflows — process edited messages, channel posts, or chat-join requests depending on what your bot does.

Configuring the webhook in Telegram

There is no dashboard — you call the Bot API. setWebhook takes the public HTTPS url, an optional secret_token (strongly recommended, covered below), an optional allowed_updates list to receive only the update types you handle, and a max_connections hint. Telegram requires HTTPS and accepts only a small set of ports (commonly 443, 80, 88, and 8443). getWebhookInfo reports the current registration and its health; deleteWebhook removes it.

The Bot API is stable but its details do change, so confirm current parameters against Telegram’s setWebhook documentation. Only one delivery method can be active at a time: setting a webhook disables getUpdates, and vice versa.

Telegram delivers updates in order and waits for your response, so a slow handler holds up the queue. Acknowledge fast and process asynchronously:

  1. Check the X-Telegram-Bot-Api-Secret-Token header (see below) and reject mismatches.
  2. Enqueue the update keyed by update_id, and return 2xx promptly.
  3. Do the real work — API calls, database writes — off the request path.
  4. Use allowed_updates at registration so you never receive types you ignore.

Because Telegram sends updates sequentially and waits, doing heavy work synchronously slows every subsequent update. HookWatch endpoints can filter by event type as an extra edge control, but allowed_updates is your first line — set it narrowly.

Verifying authenticity

Telegram does not sign updates, so you cannot verify an HMAC. Authenticity rests on two things:

  1. A secret token. When you pass secret_token to setWebhook, Telegram sends it back on every request in the X-Telegram-Bot-Api-Secret-Token header. Compare it, in constant time, against the value you set and reject anything that does not match.
  2. A secret URL. Because there is no signature, treat the webhook URL itself as a credential — a long, unguessable path — so an attacker cannot even find the endpoint to probe it.
POST /tg/8f3c1a9e2b7d4f60 HTTP/1.1
Host: bot.example.com
Content-Type: application/json
X-Telegram-Bot-Api-Secret-Token: 9d4f2c7a1e8b6033f5a...

{"update_id":874216531,"message":{"message_id":42,"text":"/start"}}

Reject any request whose secret-token header is missing or wrong before you parse the body. With no signature to verify, this check plus a secret URL and HTTPS is the whole of your authenticity story. HookWatch’s inbound signature schemes are HMAC-based and so do not apply to Telegram’s unsigned updates; the secret-token header is the mechanism to rely on here.

Common failure scenarios

  • Not HTTPS, or a rejected certificate — Telegram requires a valid public HTTPS endpoint; a self-signed or incomplete chain (without the documented handling) fails to deliver.
  • Growing pending_update_countgetWebhookInfo shows updates queuing because your endpoint is failing or too slow; Telegram holds them and keeps retrying.
  • A revealing last_error_messagegetWebhookInfo reports the last delivery error (a connection refusal, a TLS problem, a non-2xx). Read it first; it usually names the cause.
  • Silent method conflict — calling getUpdates while a webhook is set (or the reverse) means updates seem to vanish.
  • Slow handler stalling the queue — synchronous work delays every following update because delivery is ordered.

Testing workflow

getWebhookInfo is the primary diagnostic: it returns the current url, pending_update_count, last_error_date, and last_error_message, which together tell you whether Telegram can reach you and what went wrong last. There is no “resend” button — Telegram keeps retrying pending updates on its own — so fixing the endpoint and watching pending_update_count drain is the loop.

For local development, expose your handler with a tunnel and point setWebhook at the public HTTPS URL so real Telegram updates reach your machine. See testing webhooks locally; hookwatch tunnel forwards captured deliveries to a local process so Telegram can reach your laptop through a HookWatch endpoint over public TLS. Remember to deleteWebhook (or reset it) when you switch back to your deployed bot.

Retry behaviour

Telegram queues undelivered updates and keeps retrying, holding them until your endpoint accepts them or they age out, rather than dropping them immediately on the first failure. The exact retention and retry timing can change, so rely on the behaviour — a full queue that drains once you recover — and confirm current details in the Bot API docs. For your handler:

  • Return 2xx quickly; a non-2xx or a timeout leaves the update queued and blocks the ones behind it, since delivery is ordered.
  • A backed-up pending_update_count is the signal that your endpoint, not Telegram, is the problem.
  • Once you recover, expect a burst as the queue drains — your handler must tolerate that rate.

The general principles are in retry design for webhook consumers.

Idempotency

Dedupe on update_id, which is unique per update and increases monotonically. Because Telegram will resend a queued update until you acknowledge it, the same update_id can arrive more than once — record processed ids and make the effect idempotent so you do not answer one /start twice or double-post a reply. A duplicate you have already handled should still return 2xx. Full patterns are in processing duplicates exactly once.

Debugging checklist

  1. Call getWebhookInfo and read url, pending_update_count, and last_error_message.
  2. Confirm the endpoint is public HTTPS on an accepted port with a valid certificate chain.
  3. Verify the X-Telegram-Bot-Api-Secret-Token header is present and matches what you set.
  4. Confirm no getUpdates call is competing with the webhook.
  5. Check the handler returns 2xx quickly so the ordered queue does not stall.
  6. Confirm allowed_updates includes the types you expect and excludes the ones you do not.
  7. Match application logs to the update by update_id.
  8. Confirm duplicate updates are deduped and still answered 2xx.
  9. Watch pending_update_count drain after a fix to confirm recovery.

See also Telegram webhooks and a Telegram webhook not receiving updates for Telegram-specific detail, and the general debugging walkthrough.

How HookWatch helps

Because Telegram gives you only getWebhookInfo and a last_error_message to work with, a record of the actual requests is worth a lot. Point a HookWatch endpoint at your bot and every update is captured whole — the X-Telegram-Bot-Api-Secret-Token header, the update_id, the body, your response, and each attempt — so a failed callback_query still shows exactly what Telegram sent and what your handler returned. Replay a captured update against a fixed handler instead of waiting for the queue, pause an endpoint while you deploy and resume it, and filter to the update types you handle. Repeated failures group into an incident, so a stalled bot is one alert with the evidence attached rather than a silently growing queue.

Get started

See what happened to every webhook.

HookWatch keeps the request, the response, and every attempt for each delivery — so the debugging, retry, and replay steps in this article are a matter of reading, not reconstructing.