ExGram supports two methods for receiving updates from Telegram: Polling and Webhooks.
Polling Mode
Polling is the easiest way to get your bot running. The bot periodically calls getUpdates on the Telegram Bot API to retrieve new messages.
Best for:
- Development and testing
- Simple deployments
- Bots that don't need instant responses
- Environments where you can't expose a public URL
Basic Polling Setup
# In your application supervision tree
children = [
ExGram,
{MyBot, [method: :polling, token: "YOUR_TOKEN"]}
]Configuring allowed_updates
By default, Telegram sends all update types. You can filter to only the types you need:
In supervision tree:
{MyBot, [
method: {:polling, allowed_updates: ["message", "edited_message", "callback_query"]},
token: "YOUR_TOKEN"
]}In application config:
config :ex_gram, :polling,
allowed_updates: ["message", "edited_message", "callback_query"]Supervision tree options override config file settings, allowing per-bot customization.
Available Update Types
"message"- New incoming messages"edited_message"- Edited messages"channel_post"- Channel posts"edited_channel_post"- Edited channel posts"inline_query"- Inline queries"chosen_inline_result"- Inline query results chosen by user"callback_query"- Callback button presses"shipping_query"- Shipping query for payments"pre_checkout_query"- Pre-checkout query for payments"poll"- Poll state updates"poll_answer"- User's poll answer
Webhook Cleanup
By default, polling mode deletes any existing webhook. If you've never used webhooks, you can skip this:
config :ex_gram, :polling,
allowed_updates: ["message"],
delete_webhook: falseWebhook Mode
Webhooks provide real-time updates. Telegram sends updates to your server via HTTP POST requests.
Best for:
- Production deployments
- Bots requiring instant responses
- High-traffic bots
- Efficient resource usage
Prerequisites
- A public HTTPS URL (HTTP not supported by Telegram)
- Valid SSL certificate (self-signed works)
Plugandplug_cowboydependencies
Setup
1. Add dependencies:
def deps do
[
# ... other deps
{:plug_cowboy, "~> 2.0"},
{:plug, "~> 1.0"}
]
end2. Add ExGram.Plug to your router:
defmodule MyApp.Router do
use Plug.Router
plug ExGram.Plug
# Your other routes...
endThe webhook endpoint will be at /telegram/<bot_token_hash> or you can configure your custom path in the configuration, see Webhook Configuration section
3. Configure your bot:
children = [
ExGram,
{MyBot, [method: :webhook, token: "YOUR_TOKEN"]}
]Webhook Configuration
In Config File
If you configure your webhook options globally, all your bots using webhook will use the same configuration, but they will be independent.
config :ex_gram, :webhook,
url: "https://bot.example.com",
path: "/your/own/path",
allowed_updates: ["message", "callback_query"],
certificate: "priv/cert/selfsigned.pem",
drop_pending_updates: false,
ip_address: "1.1.1.1",
max_connections: 50,
secret_token: "your_secret_here"In Supervision Tree
You can configure it on the supervision tree instead of the global config, to have different configurations per bot for example.
If you configure it this way, and setup a custom path, you have to also configure the plug to use that path.
# application.ex
webhook_options = [
url: "https://bot.example.com",
path: "/custom/path",
allowed_updates: ["message", "callback_query"],
secret_token: System.get_env("WEBHOOK_SECRET")
]
children = [
ExGram,
{MyBot, [method: {:webhook, webhook_options}, token: "YOUR_TOKEN"]}
]
# router.ex
plug ExGram.Plug, path: "/custom/path"Webhook Options
| Option | Type | Description |
|---|---|---|
url | String | Required. Your bot's public HTTPS URL (with scheme and optional port) |
path | String | The path that will be used as a callback. If not provided "/telegram" is used. |
allowed_updates | List of strings | Update types to receive (same as polling) |
certificate | String | Path to self-signed certificate file |
drop_pending_updates | Boolean | Drop updates that arrived while bot was down |
ip_address | String | Fixed IP address for Telegram to use |
max_connections | Integer | Max simultaneous connections (1-100, default 40) |
secret_token | String | Secret token to verify requests are from Telegram |
See Telegram setWebhook docs for detailed explanations.
Using Self-Signed Certificates
For development or internal deployments:
config :ex_gram, :webhook,
url: "https://bot.example.com:8443",
certificate: "priv/cert/selfsigned.pem"Generate a self-signed certificate:
openssl req -newkey rsa:2048 -sha256 -nodes -keyout private.key -x509 -days 365 -out cert.pem
Secret Token Verification
Add a secret token for additional security:
config :ex_gram, :webhook,
url: "https://bot.example.com",
secret_token: System.get_env("WEBHOOK_SECRET_TOKEN")Telegram will send this token in the X-Telegram-Bot-Api-Secret-Token header.
Test Environment
Telegram provides a Test Environment for testing your bot without affecting production.
Enable it in config:
config :ex_gram, test_environment: trueNote: You'll need a separate bot token from the test environment's BotFather.
Choosing Between Polling and Webhooks
| Feature | Polling | Webhooks |
|---|---|---|
| Setup complexity | Simple | Moderate (requires HTTPS) |
| Real-time updates | Delayed (polling interval) | Instant |
| Server requirements | None | Public HTTPS endpoint |
| Resource usage | Constant (polling loop) | On-demand (per update) |
| Best for | Development, simple bots | Production, high-traffic |
| Network | Works behind firewall/NAT | Requires public IP |
Multiple Bots with Different Methods
You can run different bots with different update methods:
children = [
ExGram,
{MyBot.DevBot, [method: :polling, token: dev_token]},
{MyBot.ProdBot, [method: :webhook, token: prod_token]},
{MyBot.OtherBot, [method: :webhook, token: other_token]}
]See Multiple Bots for more details.
Next Steps
- Sending Messages - Learn the DSL for building responses
- Multiple Bots - Run multiple bots in one application
- Fly.io Deployment - Deploy your webhook bot to production