How the webhook URL works. It is a public address that records whatever is sent to it and
answers 200. An inbox keeps the 50 most recent requests, accepts bodies up to 256 KB, and is
deleted 24 hours after it was created — or sooner if you press Delete URL or our server
restarts, because requests are held in memory and never written to disk.
Anyone who has the URL can read what it received. Use sandbox and test-mode accounts, do not route production traffic or customer data through it, and delete it when you are done. Privacy details.
What is a Telegram webhook?
A Telegram bot receives updates in one of two ways: by polling getUpdates, or by registering a webhook with setWebhook, after which Telegram sends an HTTPS POST to your URL for every update. The two are mutually exclusive — while a webhook is set, getUpdates returns an error.
Each request carries one JSON Update. It has an update_id and exactly one other field naming what happened: message, edited_message, callback_query, inline_query and so on.
What a Telegram webhook request looks like
The request line and headers as they reach your server, followed by the body. Ids, tokens and the signature are placeholders.
POST /your/webhook/path HTTP/1.1
Host: your-server.example
Content-Type: application/json
X-Telegram-Bot-Api-Secret-Token: a-secret-you-chose-1234
{
"update_id": 100000001,
"message": {
"message_id": 1365,
"from": {
"id": 1111111,
"is_bot": false,
"first_name": "Jon",
"username": "jon_example",
"language_code": "en"
},
"chat": {
"id": 1111111,
"first_name": "Jon",
"username": "jon_example",
"type": "private"
},
"date": 1767225600,
"text": "/start",
"entities": [
{
"offset": 0,
"length": 6,
"type": "bot_command"
}
]
}
}
Optional fields are simply absent — a photo message has no text, a channel post has no from. chat.id is what you pass back as chat_id to reply; for groups and channels it is a large negative number, so store it in a 64-bit type. Commands are not special: /start is ordinary text with a bot_command entity.
Headers Telegram sends
| Header | Example | What it is |
|---|---|---|
Content-Type | application/json | The body is a JSON Update. |
X-Telegram-Bot-Api-Secret-Token | a-secret-you-chose-1234 | Sent only if you passed secret_token to setWebhook. The value is exactly what you set. |
Header names are case-insensitive. Your framework may show them lower-cased, and HTTP/2 always does.
Authenticating the request
Telegram does not sign the body. Authentication is a shared secret in a header: pass secret_token when calling setWebhook (1–256 characters from letters, digits, underscore and hyphen), and every webhook request then carries it in X-Telegram-Bot-Api-Secret-Token. Reject any request where it is missing or different.
Before that option existed, the usual advice was to put the bot token in the webhook path so the URL was unguessable. Prefer the header: URLs end up in access logs, the bot token is a far more valuable secret than a webhook token, and rotating one should not mean rotating the other.
const crypto = require('crypto');
function fromTelegram(req, secretToken) {
const a = Buffer.from(req.headers['x-telegram-bot-api-secret-token'] || '');
const b = Buffer.from(secretToken);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
Responses, retries and replying inline
Return a 2xx. On anything else Telegram repeats the update, giving up after what its documentation calls "a reasonable amount of attempts", and updates are kept for at most a day. getWebhookInfo is the debugging tool: it reports pending_update_count and the time and text of the last delivery error.
A Telegram-specific shortcut: the response to a webhook can itself be an API call. Reply with JSON containing a method field and its parameters — {"method":"sendMessage","chat_id":1111111,"text":"Hi"} — and Telegram executes it, saving a round trip. You do not learn whether that call succeeded, so use it for simple acknowledgements.
Catch a real Telegram update
- Press Create webhook URL at the top of this page and copy the address.
- Call
setWebhookfor a test bot:https://api.telegram.org/bot<TOKEN>/setWebhook?url=<the address>&secret_token=test123. - Send the bot a message in Telegram.
- Read the Update and the secret-token header in the inbox above.
- When finished, call
deleteWebhookso the bot can usegetUpdatesagain.
Use a throwaway bot for this. While the webhook points here, this inbox — and anyone holding its URL — receives every message sent to the bot, and your real bot receives none.
To run a caught request against your own code, select it and press Open in API Tester: the method, headers and body are loaded into the request console, where you enter your server's public URL and send it. A body you edit will no longer match its signature — that is the signature doing its job.
Things that catch people out
- Only certain ports. Telegram delivers webhooks to ports 443, 80, 88 and 8443 only, and requires TLS with a valid certificate (or a self-signed one you upload).
allowed_updatesis sticky. The list you pass tosetWebhookpersists until you pass another, so a bot can silently stop receiving a type of update that was never re-enabled.- One slow handler blocks the queue. Raise
max_connectionsif needed, and acknowledge quickly — pending updates pile up behind a stuck one.
api-tester.com is independent and is not affiliated with Telegram. Providers change header names, limits and retry schedules; treat the official Telegram documentation as the source of truth.
Related examples and tools
SendGrid Webhook Example
SendGrid's Event Webhook posts a batch — a JSON array of email events — and signs it with ECDSA rather than an HMAC.
WooCommerce Webhook Example
What a WooCommerce store posts when an order, product or customer changes — the X-WC-Webhook-* headers, the REST-shaped JSON, and the rule that switches a failing webhook off.
Mailchimp Webhook Example
Mailchimp audience webhooks are form posts with bracketed field names, and they carry no signature at all.
HubSpot Webhook Example
HubSpot delivers CRM events in batches: one request, a JSON array, each element a small notification that names a record without containing it.
Square Webhook Example
Square signs the notification URL together with the body, so a request that is perfectly genuine fails verification when your server disagrees about its own address.
Telegram Bot API Tester
Call getMe, setWebhook and getWebhookInfo from the browser.
Curl POST JSON
Send a JSON body like this one from a terminal.
Webhook Tester
The inbox on its own page, plus a guide to signatures, retries and idempotent handlers.
All Webhook Examples
30 providers side by side: content type, signature scheme and retry behaviour.
Other webhooks that send a plain secret token
References
- Official Telegram documentation — The provider's own guide to the requests described on this page.
- Telegram: guide to webhooks — Telegram's own walk-through of ports, certificates and troubleshooting.
- Telegram Bot API — The full reference, including the Update object a webhook delivers.
- Node.js: crypto.timingSafeEqual() — The constant-time comparison used in the code on this page.
These links leave api-tester.com, which is independent of the companies and projects they point to.
Telegram webhook FAQ
What does a Telegram webhook payload look like?
A JSON Update object with an update_id and one field describing the event, most often message, which contains message_id, from, chat, date and the text or media.
How do I secure a Telegram bot webhook?
Pass a secret_token when calling setWebhook. Telegram then sends it in the X-Telegram-Bot-Api-Secret-Token header of every request, and your endpoint rejects requests without it.
Can I use getUpdates and a webhook together?
No. They are mutually exclusive. While a webhook is set, getUpdates fails; call deleteWebhook to switch back to polling.
Which ports does Telegram support for webhooks?
Ports 443, 80, 88 and 8443, over HTTPS.
How do I see why Telegram webhook deliveries fail?
Call getWebhookInfo. It returns the configured URL, the number of pending updates and the date and message of the most recent delivery error.