Mimir normally receives Telegram updates by long polling. Set
TELEGRAM_WEBHOOK_URL to make it register a webhook instead. The public HTTPS
endpoint and the existing health endpoints share one Node HTTP server and one
HEALTH_PORT; webhook mode does not start a second listener.
sequenceDiagram
participant Telegram as Telegram Bot API
participant Proxy as Public HTTPS proxy
participant Health as Health HTTP server
participant Grammy as grammY webhookCallback
Telegram->>Proxy: POST /telegram-webhook<br/>X-Telegram-Bot-Api-Secret-Token: per-boot secret
Proxy->>Health: Forward path, JSON body, and secret header
Health->>Grammy: IncomingMessage and ServerResponse
Grammy->>Grammy: Verify secret and handle the update
Grammy-->>Health: 200, or 401 for a wrong secret
Health-->>Proxy: HTTP response
Proxy-->>Telegram: HTTP response
The implementation has four steps:
- When
TELEGRAM_WEBHOOK_URLis set,src/index.tsgenerates 32 random bytes and hex-encodes them as a per-process secret. - The same secret is passed to grammY as
secretTokenand to Telegram'ssetWebhookcall assecret_token. - Telegram includes the secret in the
X-Telegram-Bot-Api-Secret-Tokenrequest header. grammY compares that exact header with the generated value and returns401when it is absent or does not match. src/health.tssends onlyPOST /telegram-webhookto the grammY handler. The same listener continues to serveGET /health,GET /healthz,GET /health/live, and the other operational routes documented in the README.
The secret is generated again on every process start and is not read from an
environment variable or written to disk. Each startup calls setWebhook with
the new value before logging that webhook setup succeeded. A proxy must
therefore forward X-Telegram-Bot-Api-Secret-Token unchanged; it must not
replace it with a fixed local secret. A request still in flight with the
previous process's secret can receive 401 after a restart.
Set TELEGRAM_WEBHOOK_URL to the public HTTPS origin that reaches this process:
TELEGRAM_WEBHOOK_URL=https://bot.example.com
HEALTH_HOST=127.0.0.1
HEALTH_PORT=8787The configured value must be an HTTP or HTTPS URL, as enforced by
src/config.ts. Telegram needs a publicly
reachable HTTPS URL in normal deployments. Mimir resolves
/telegram-webhook from the URL root, replacing any path already present in
TELEGRAM_WEBHOOK_URL. The example above registers:
https://bot.example.com/telegram-webhook
Do not set HEALTH_PORT=0 in webhook mode because that disables the shared
listener. Keep the default loopback HEALTH_HOST=127.0.0.1 when the reverse
proxy runs on the same host. In a container or when the proxy reaches the
process over a container network, bind an address it can reach, usually
HEALTH_HOST=0.0.0.0. When a platform supplies PORT and HEALTH_PORT is
unset, Mimir uses PORT for this listener.
Terminate TLS at the public proxy and forward this route to the listener without rewriting its method, path, body, or secret header:
HTTPS POST /telegram-webhook
-> http://127.0.0.1:8787/telegram-webhook
The listener also contains operational endpoints. Configure the proxy to
publish only /telegram-webhook unless /health or another route is
intentionally public. No Telegram update route exists at /, /webhook, or
/health.
After a restart:
- Confirm the boot log reports
mode=webhookand the redacted webhook URL. - Confirm the proxy sends
POST /telegram-webhookto the same port that answersGET /health. - Confirm successful Telegram deliveries return a 2xx response. A
401means the proxy omitted or changedX-Telegram-Bot-Api-Secret-Token, or a request used the secret from a previous process. A404usually means the method or path was rewritten.
Mimir awaits Telegram's setWebhook request during startup. If the Bot API
call fails or Telegram rejects the URL, startup does not silently fall back to
long polling.
- Stop the webhook deployment from accepting new traffic.
- Using the bot token through existing secret-management tooling, call
Telegram's Bot API
deleteWebhookmethod. Leavedrop_pending_updatesfalse or omitted unless discarding queued updates is an intentional operational decision. - Call
getWebhookInfoand confirm itsurlfield is empty. - Remove
TELEGRAM_WEBHOOK_URLfrom the process environment and restart Mimir.
The application source does not make an explicit deleteWebhook call during
this mode switch. With TELEGRAM_WEBHOOK_URL unset,
src/index.ts starts grammY's long-polling loop.
The health listener can remain enabled for readiness and liveness checks; it
no longer accepts POST /telegram-webhook and returns 404 for that path.
Do not run webhook and long-polling instances for the same bot token at the same time. Telegram permits only one update-delivery mode for a bot, and each new webhook registration or long-polling startup replaces the previous mode.