Complete Guide to OpenClaw Telegram Integration
Learn how to create a Telegram bot and connect it to OpenClaw. Full tutorial covering bot creation, configuration, commands, and group chat setup.
OpenClaw Manuals
Tutorial Authors
Overview
Telegram integration lets you interact with OpenClaw through a dedicated Telegram bot. OpenClaw uses the grammY framework for long-polling by default, with optional webhook support for production deployments.
Prerequisites
- OpenClaw installed and running
- A Telegram account
Step 1: Create a Telegram Bot
Talk to BotFather
-
Open Telegram and search for
@BotFather -
Start a chat and send
/newbot - Follow the prompts:
You: /newbot
BotFather: Alright, a new bot. How are we going to call it?
Please choose a name for your bot.
You: My OpenClaw Assistant
BotFather: Good. Now let's choose a username for your bot.
It must end in `bot`. Like this, for example:
TetrisBot or tetris_bot.
You: myopenclaw_bot
BotFather: Done! Congratulations on your new bot. You will find it at
t.me/myopenclaw_bot. You can now add a description, about
section and profile picture for your bot.
Use this token to access the HTTP API:
123456789:ABCdefGHIjklMNOpqrsTUVwxyz
Keep your token secure and store it safely.
Save the bot token — you'll need it in the next step.
Disable Privacy Mode (Recommended for Groups)
If you plan to use the bot in group chats, disable privacy mode so it can read all messages:
/setprivacy @myopenclaw_bot Disable
After changing privacy mode, you must remove and re-add the bot to existing groups for the change to take effect. Alternatively, promote the bot to group admin — admin bots always receive all messages.
Step 2: Configure OpenClaw
Token Storage
You can store the bot token in two ways. Config takes precedence over the environment variable.
Option A — Environment variable:
# Add to ~/.openclaw/.env TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz
Option B — Direct in config:
// ~/.openclaw/openclaw.json
{
channels: {
telegram: {
botToken: "123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
}
}
}
Minimal Configuration
Edit
~/.openclaw/openclaw.json
:
{
channels: {
telegram: {
// Token from env TELEGRAM_BOT_TOKEN or set botToken directly
}
}
}
That's all you need — OpenClaw starts long-polling automatically when a Telegram channel is configured.
Step 3: Test Your Bot
- Open Telegram and search for your bot username
-
Start a chat with
/start - Send any message to test
Access Control — DM Policy
Control who can message your bot in private chats via
dmPolicy
:
| Policy | Behavior |
|--------|----------|
|
"pairing"
(default) | Unknown senders get an expiring pairing code; you approve via CLI |
|
"allowlist"
| Only user IDs / @usernames in
allowFrom
can message |
|
"open"
| Anyone can message the bot |
|
"disabled"
| DMs are turned off entirely |
Pairing Mode (Default)
When a new user messages the bot, they receive a pairing code. Approve it with:
# List pending pairing requests
openclaw pairing list telegram
# Approve a specific code
openclaw pairing approve telegram
Pairing codes expire after 1 hour.
Allowlist Mode
{
channels: {
telegram: {
dmPolicy: "allowlist",
allowFrom: [123456789, "@trusted_user"]
}
}
}
Open Mode
{
channels: {
telegram: {
dmPolicy: "open"
}
}
}
Group Chat Support
Enable Group Access
Add group IDs to the
groups
object. Use
"*"
to allow all groups:
{
channels: {
telegram: {
groups: {
"-1001234567890": {}, // specific group with defaults
"-1009876543210": { // specific group with overrides
groupPolicy: "open",
requireMention: false,
systemPrompt: "You are a helpful coding assistant."
}
}
}
}
}
Or allow all groups:
{
channels: {
telegram: {
groups: "*"
}
}
}
Group Policy
Control who can interact with the bot within groups:
| Setting | Description |
|---------|-------------|
|
groupPolicy: "open"
| Any group member can message the bot |
|
groupPolicy: "allowlist"
| Only senders in
groupAllowFrom
can interact |
|
groupPolicy: "disabled"
| Bot ignores all group messages |
{
channels: {
telegram: {
groupPolicy: "allowlist",
groupAllowFrom: [123456789, "@allowed_user"]
}
}
}
Mention Requirement
By default, the bot requires an
@mention
in groups. You can change this per-group:
{
channels: {
telegram: {
groups: {
"-1001234567890": {
requireMention: false // bot responds to all messages
}
}
}
}
}
Or use the session-only command
/activation always
in the group chat.
Per-Group Overrides
Each group entry supports these fields:
| Field | Description |
|-------|-------------|
|
groupPolicy
| Override the global group policy |
|
requireMention
| Whether @mention is required |
|
skills
| Skills available in this group |
|
allowFrom
| Sender allowlist for this group |
|
systemPrompt
| Custom system prompt for this group |
|
enabled
| Enable/disable for this specific group |
Message Format & Chunking
HTML Parse Mode
OpenClaw sends messages using Telegram's
HTML
parse mode (not Markdown). Markdown from the LLM is automatically converted to HTML-safe tags. If Telegram rejects the HTML, the message is retried as plain text.
Text Chunking
Long messages are split into multiple Telegram messages:
{
channels: {
telegram: {
textChunkLimit: 4000, // max chars per message (default: 4000)
chunkMode: "newline" // "length" (default) or "newline"
}
}
}
-
"length"— splits at the character limit -
"newline"— splits at paragraph boundaries before applying the length limit
Media Handling
Media Size Limit
{
channels: {
telegram: {
mediaMaxMb: 5 // max file size in MB (default: 5)
}
}
}
Stickers
- Static stickers (WEBP) are processed through vision and described to the LLM
- Animated / video stickers are skipped
-
Sticker descriptions are cached in
~/.openclaw/telegram/sticker-cache.jsonto avoid redundant vision calls
To enable the bot to send stickers:
{
channels: {
telegram: {
actions: {
sticker: true // default: false
}
}
}
}
History & Context
{
channels: {
telegram: {
historyLimit: 50, // group context (default: 50)
dmHistoryLimit: 100 // DM context
}
}
}
Per-user DM override:
{
channels: {
telegram: {
dms: {
"123456789": {
historyLimit: 200
}
}
}
}
}
Set to
0
to disable history.
Streaming
OpenClaw supports draft-based streaming in private chats (with forum topics enabled):
| Setting | Description |
|---------|-------------|
|
streamMode: "off"
| Streaming disabled (default) |
|
streamMode: "partial"
| Continuous updates to a draft message |
|
streamMode: "block"
| Chunked updates to a draft message |
Block mode settings:
{
channels: {
telegram: {
streamMode: "block",
draftChunk: {
minChars: 200,
maxChars: 800,
breakPreference: "paragraph"
}
}
}
}
Webhook Mode (Production)
For production deployments, use webhooks instead of long-polling:
{
channels: {
telegram: {
webhookUrl: "https://your-domain.com/telegram-webhook",
webhookSecret: "your-random-secret-string",
webhookPath: "/telegram-webhook" // local path, default: /telegram-webhook
}
}
}
The webhook listener binds to
0.0.0.0:8787
. When
webhookUrl
is set, OpenClaw automatically switches from polling to webhook mode.
Commands
Native Commands
OpenClaw registers these commands with Telegram's bot menu on startup:
| Command | Description |
|---------|-------------|
|
/start
| Welcome message |
|
/status
| Show bot status |
|
/reset
| Reset conversation |
|
/model
| Show / switch model |
Custom Commands
Add menu entries via config (menu-only — they don't execute unless handled elsewhere):
{
channels: {
telegram: {
customCommands: [
{ command: "weather", description: "Get weather forecast" },
{ command: "translate", description: "Translate text" }
]
}
}
}
Command names must be lowercase
a-z0-9_, 1–32 characters. Leading/is stripped automatically. Cannot override native commands.
Inline Buttons
Control inline button availability:
| Setting | Scope |
|---------|-------|
|
capabilities.inlineButtons: "off"
| Disabled |
|
capabilities.inlineButtons: "dm"
| DMs only |
|
capabilities.inlineButtons: "group"
| Groups only |
|
capabilities.inlineButtons: "all"
| Both DMs and groups |
|
capabilities.inlineButtons: "allowlist"
| Both + sender filtering (default) |
Network & Proxy
{
channels: {
telegram: {
timeoutSeconds: 500, // Bot API request timeout (default: 500)
network: {
autoSelectFamily: false // disable Happy Eyeballs (default on Node 22)
},
proxy: "socks5://127.0.0.1:1080" // SOCKS/HTTP proxy for Bot API
}
}
}
Troubleshooting
Bot Not Responding
- Verify token is correct:
curl "https://api.telegram.org/bot/getMe"
- Check OpenClaw logs:
openclaw logs --follow
Messages Not Reaching in Groups
-
Privacy mode must be disabled (
/setprivacy→ Disable) or bot must be admin -
Test with
/activation always(session-only); persist viarequireMention: falsein config
IPv6 Routing Failures
The bot may silently stop responding if IPv6 routing fails. Check DNS for
api.telegram.org
:
dig api.telegram.org AAAA
Fix by enabling IPv6 egress or forcing IPv4:
# Add to /etc/hosts 149.154.167.220 api.telegram.org
Or use the network config:
{
channels: {
telegram: {
network: {
autoSelectFamily: false
}
}
}
}
Long-Polling Aborts (Node 22+)
Node 22 is stricter with
AbortSignal
instances. Upgrade OpenClaw to the latest version or downgrade to Node 20.
setMyCommands
Failures
Outbound HTTPS/DNS to
api.telegram.org
may be blocked. Check your firewall rules.
Security Best Practices
- Never share your bot token — revoke immediately via BotFather if compromised
- Use pairing or allowlist for DM access control
- Set group policies to control who can interact in groups
-
Find user IDs safely
via
openclaw logs --followor the Bot APIgetUpdatesendpoint — avoid third-party ID bots - Use webhooks with a secret for production deployments