TelegramPHP Documentation

Webhooks

Polling asks Telegram for updates; a webhook has Telegram post them to you. Either is a complete way to run a bot, and the choice is mostly about where the bot lives:

Long polling Webhook
Needs no public address - works from a laptop, behind NAT. Needs a public HTTPS endpoint.
One process holds the token. Scales to several processes behind a load balancer.
Updates arrive within the poll round. Updates arrive as they happen.
Costs a held-open connection. Costs a running web server.

Start with polling. Move to a webhook when you have somewhere to host one.

Listening

Give the client a webhook option and it listens instead of polling:

$telegram = new Telegram([
    'token' => $token,
    'webhook' => [
        'listen' => '0.0.0.0:8080',
        'path' => '/hook',
        'secret_token' => $secret,
    ],
]);

$telegram->run();

Updates then reach your handlers exactly as they would under polling - see Updates and events. Nothing else about your bot changes.

Key Default Description
listen 0.0.0.0:8080 The address to bind. Any React\Socket\SocketServer address works.
path / The path deliveries are accepted on. Anything else answers 404 .
secret_token
Checked against the X-Telegram-Bot-Api-Secret-Token header.
socket_context [] Passed to the socket server, for TLS if you terminate it here.

Registering the endpoint

Telegram needs to be told where to deliver, which is a call like any other. Doing it on ready means the listener is already up when Telegram starts posting:

$telegram->on(Event::READY, function (Telegram $telegram) use ($url, $secret) {
    $telegram->setWebhook($url, secret_token: $secret, allowed_updates: [Event::MESSAGE]);
});

To go back to polling, remove it - Telegram refuses to serve getUpdates while a webhook is set:

$telegram->deleteWebhook(drop_pending_updates: true);

getWebhookInfo tells you what Telegram currently thinks, including why deliveries are failing:

$telegram->getWebhookInfo()->then(function (WebhookInfo $info) {
    $info->url;                    // '' when none is set
    $info->pending_update_count;
    $info->last_error_message;     // why the last delivery failed
});

TLS

Telegram only delivers to HTTPS, and the certificate must be valid or self-signed and uploaded with setWebhook . In practice the listener sits behind a reverse proxy that terminates TLS:

location /hook {
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header X-Telegram-Bot-Api-Secret-Token $http_x_telegram_bot_api_secret_token;
}

Make sure the proxy forwards that header - it is the only thing separating a real delivery from anyone who guesses the URL.

The secret token

Set one. Your endpoint is a public URL that accepts JSON claiming to be from Telegram, and the secret header is what makes that claim checkable. Deliveries that fail the check are answered 403 and never reach your handlers.

$secret = bin2hex(random_bytes(16));   // store it; pass the same value both places

What the listener answers

Situation Response
A valid delivery 200 {"ok":true} , as soon as the update is queued
Wrong or missing secret 403
Another path, or a GET 404
A body that is not JSON 400
Your handler threw 200 anyway

That last row is deliberate. Telegram redelivers anything that is not a 2xx , and redelivery will not fix a bug in a handler - it will only replay it. The exception is logged and emitted as Event::ERROR instead.

Answer quickly. Telegram waits only a few seconds and retries on timeout, so do the work after answering rather than before - which is what the promise-based API does by default.

Search results