Telegram Webhook secret_token vs HMAC: What Your Receiver Must Verify
Telegram Bot API’s secret_token is not an HMAC signature. Set it through setWebhook, and Telegram includes the same value in X-Telegram-Bot-Api-Secret-Token on webhook requests. Your receiver checks that value against its configured secret. A UnifyPort webhook uses a different contract: when signing is enabled, verify X-Device-Signature against HMAC-SHA256 of the timestamp and exact request-body bytes. These checks are not interchangeable.
The distinction that matters
- Telegram’s secret header is a shared credential sent with the request, not a digest calculated from the JSON body.
- UnifyPort’s signature binds the raw body and
X-Device-Timestampto the endpoint’ssigning_secret. - Both receivers still need HTTPS, protected secrets, and duplicate-safe processing.
- Select the verification contract from trusted route configuration—not from an unverified JSON
providerfield or whichever header happens to be present.
This article concerns request authentication, not the choice of messaging identity. If that decision is still open, start with Telegram Bot API webhook vs unified inbound webhook.
What Telegram secret_token actually verifies
The official Telegram Bot API reference documents secret_token as an optional setWebhook parameter. It accepts 1–256 characters from A-Z, a-z, 0-9, _, and -. When configured, Telegram sends the value in X-Telegram-Bot-Api-Secret-Token with every webhook request.
For a receiver that requires this protection, the rule is simple: a missing or mismatched header must not enter the trusted processing queue. An exact match proves possession of the configured value. It does not establish a separate cryptographic relationship between that value and the request body.
That distinction is important at a proxy or forwarding service. If a component can read the token, it can present the same credential with a different body. HTTPS protects the transport connection; the static header itself does not detect body changes after TLS termination. This is a trust-boundary distinction, not a reason to abandon the official Bot API.
Treat the webhook secret as a dedicated credential. Do not reuse the bot token or paste the webhook secret into a public request-capture service. Keep it out of access logs, tracing exports, and support screenshots.
Static secret header vs signed body
HMAC is a keyed message-authentication mechanism. In UnifyPort’s documented implementation, the receiver recomputes a digest over a defined string rather than comparing the request header directly with the secret.
| Question | Telegram Bot API webhook | Signed UnifyPort webhook |
|---|---|---|
| Configuration | setWebhook.secret_token | Endpoint signing_secret |
| Header to verify | X-Telegram-Bot-Api-Secret-Token | X-Device-Signature |
| Value received | The configured token itself | Hex-encoded HMAC-SHA256 digest |
| Body included in this check? | No | Yes, the exact raw bytes |
| Timestamp included in this check? | No | Yes, X-Device-Timestamp |
| Receiver operation | Compare the header with the configured token | Recompute and compare the digest |
| Guarantees one-time processing? | No | No |
For UnifyPort, the signed input is exactly:
<X-Device-Timestamp>.<raw request body>
The timestamp is RFC 3339 UTC, and the dot is literal. Reformatting JSON, changing whitespace, or parsing and serializing before verification can change the signed bytes. The secret is the HMAC key; it is not the expected signature header value.
If signing_secret is empty, UnifyPort signing is disabled and X-Device-Signature is omitted. A receiver configured to require signed deliveries should reject that request rather than silently switching authentication modes.
Keep the two ingress paths separate
A practical design is to operate distinct application routes for native Telegram updates and UnifyPort events. This is an application recommendation, not an additional provider endpoint.
- Bind each route to a sender and credential. Set the expected mechanism in deployment configuration before accepting traffic.
- Authenticate before dispatch. Telegram requests must pass the secret-header check. Signed UnifyPort requests must pass the raw-body HMAC and timestamp-freshness checks.
- Validate the appropriate payload. Do not send a native Telegram
Updateto a handler expecting UnifyPort’smessage.receivedenvelope. - Store accepted work durably. Keep authentication separate from idempotency and downstream business authorization.
- Redact sensitive material. Record which check failed without recording the secret header or credentials.
Avoid “accept either header” middleware on a shared route. That design makes the weaker or accidentally enabled branch an alternative to the intended verification policy. In particular, a Telegram secret header must never substitute for the required HMAC on a UnifyPort route.
For implementation details of the second contract, the HMAC replay-protection guide covers freshness and retry-safe processing. A timestamp in a JSON message is not a substitute for a timestamp covered by the delivery signature.
Acceptance tests before production
The following are proposed tests, not reported test results. Run them in an isolated environment using credentials you control.
| Test | Expected receiver behavior |
|---|---|
| Telegram header missing or incorrect | Reject before trusted processing |
| Telegram header correct, body altered | Header check alone cannot detect the change; payload validation and the trusted transport boundary still matter |
| UnifyPort body altered after signing | Reject because the digest no longer matches |
| UnifyPort signature valid but timestamp outside your configured window | Reject under the freshness policy |
| Telegram header submitted to the UnifyPort route | Reject; do not change verification modes |
| Authentic ordinary event delivered again | Accept idempotently without repeating the business action |
Choose a freshness window for your clock accuracy and delivery conditions rather than copying an unrelated provider’s value. Do not interpret successful authentication as permission to execute every command contained in a message.
Where UnifyPort fits—and where it does not
UnifyPort’s unofficial interface provides normalized events from connected messaging accounts. Its HMAC protects the UnifyPort-to-your-receiver handoff. It is not a Telegram-native signature and does not prove end-to-end authorship by a Telegram user.
If your product is already a Telegram bot, implement Telegram’s documented protection on that ingress. There is no need to change platforms just to obtain a different authentication mechanism. If you use UnifyPort for an account-level or cross-channel queue, follow its own contract and enable signing when you create the webhook endpoint.
FAQ
Should I calculate HMAC with Telegram secret_token?
Not to verify the documented Bot API secret header. Compare that header with the configured token. Do not invent a body-signature algorithm for a header that carries the token itself.
Can I compare X-Device-Signature directly with signing_secret?
No. Recompute HMAC-SHA256 over the documented timestamp-plus-raw-body input, then compare the digest with the received hex signature.
Does either mechanism prevent duplicate processing?
No. Authentication and duplicate handling are separate. Persist accepted work and make downstream actions idempotent; HMAC alone does not enforce one-time delivery.
Next step and sources
Use the webhook delivery and signature reference as the implementation contract for a UnifyPort receiver.
Sources checked on 2026-09-17:
Turn messaging integration into a stable product pipeline.
Start by sending through one API, then bring every inbound message back into your business system with standard events.