Connecting Hermes Agent to Telegram: A First-Time Setup Guide
Step-by-step setup of the Hermes messaging gateway for Telegram: BotFather token, allow-list, home chat ID, and proof that the first message actually arrives back in the chat.

A Hermes install with no gateway configured is half an install. The same agent that answers you in the terminal can answer you from Telegram — same memory, same skills, same model — but only after you wire at least one messaging platform into the gateway. Telegram is the easiest place to start because the bot setup protocol is well-documented and the runtime fits in the free tier.
This is the layer that runs after hermes chat -q "Reply with OK." proves the agent itself works. By the end you’ll have a Telegram chat where sending a message from your phone produces a Hermes reply within seconds, and that reply will outlive the terminal session because the gateway keeps the per-chat session in SQLite.
What the gateway does (and what it doesn’t)
The gateway is a single background process that connects your Hermes install to one or more messaging platforms. Each adapter speaks that platform’s protocol — Telegram’s long-poll via getUpdates, Discord’s gateway WebSocket, etc. — and routes inbound messages through a per-chat session store into the same AIAgent your terminal uses.
What it is not: a separate agent (same model, same memory), auto-started (nothing listens until you run or install), or free-for-all (default deny — unapproved chats are logged but never replied to). Approval happens once, at setup time.
The compact command set you’ll touch:
| Command | Job |
|---|---|
hermes gateway setup | Interactive wizard for token + allow-list |
hermes gateway list | Show all platforms and their status |
hermes gateway run | Foreground bridge (use this for the first test) |
hermes gateway install | Register systemd/launchd service for autostart |
hermes gateway start / stop / restart | Control the installed service |
hermes gateway status [--deep] | Health check, adapter-by-adapter |
hermes gateway uninstall | Remove the service registration |
You will run six of these, in order, to finish this guide.
Step 1: get a Telegram bot token
Every Telegram bot is born from a chat with BotFather:
- Send
/newbot. - BotFather asks for a display name — anything; it shows up in the chat header.
- BotFather asks for a username — must end in
botand be globally unique. You can’t change it later. - BotFather returns an HTTP API token of the form
123456789:AA....
That token is the secret — anyone with it owns the bot.
| Do | Don’t |
|---|---|
Paste it directly into hermes gateway setup. | Paste it into a chat, an issue, a screenshot, or a git commit. |
Store it in ~/.hermes/.env if scripting setup. | Echo it into shell history on a shared host. |
Revoke and re-issue (/revoke → /token) if it leaks. | Treat it as low-value “because it’s just Telegram.” |
Step 2: run hermes gateway setup
The wizard is arrow-key driven:
hermes gateway setup
Pick Telegram, then answer: bot token from BotFather, your numeric user ID from @userinfobot, the home chat ID (blank → set later via /sethome), and “Start now?” → no (Step 4 does the foreground run).
For scripted provisioning, set the same fields directly. The home chat ID 8630382972 here is from the source install; substitute the chat you actually own (find it via https://api.telegram.org/bot<TOKEN>/getUpdates after sending yourself a message):
hermes config set gateway.platforms.telegram.token "$TELEGRAM_BOT_TOKEN"
hermes config set gateway.platforms.telegram.allowed_users "123456789"
hermes config set gateway.platforms.telegram.home_channel_chat_id "8630382972"
The wizard writes ~/.hermes/gateway.json (or $HERMES_HOME); the token lands in ~/.hermes/.env as TELEGRAM_BOT_TOKEN.
Step 3: confirm the allow-list is what you expect
Denial-by-default is the single biggest reason “the bot never replies.” Sanity-check with:
hermes gateway list
| Indicator | Meaning |
|---|---|
telegram - configured | Token loaded, allow-list non-empty, gateway not yet started. |
telegram - running | Foreground or service bridge is connected. |
telegram - denied | Token set but home chat not in the allow-list — check ~/.hermes/gateway.json. |
Three allow-list rules: allowed_users (required for DMs), allowed_chats (groups; IDs are negative, -100… for supergroups), and the per-group require_mention flag that defaults to true.
Step 4: foreground run for the first time
Start the bridge in the foreground so logs scroll past your terminal:
hermes gateway run
You should see, in order: adapter init lines (Telegram shows its bot username and configured chat IDs), cron scheduler started, and then the process holding the foreground. Ctrl-C to stop. Send hello from the bot chat in Telegram — you will see the inbound message, the session creation (agent:main:telegram:dm:<chat_id>), the agent turn, the outbound reply, and the Telegram API response in the same scrollback.
If multiple platforms are configured, hermes gateway run --platform telegram pins it to the one adapter. Most installs leave the default.
Step 5: send a test message (this is the proof)
Stop the foreground with Ctrl-C and restart it, then send this from your phone:
Reply with exactly: gateway smoke test OK at <current time>
If the gateway is wired correctly, the reply lands in the chat within 2-5 seconds and references the time you asked for. Three things were just proven:
- Inbound — token valid, your chat in the allow-list, long-poll alive.
- Session — agent picked the right session key; if memory is on it sees prior CLI context.
- Outbound — agent turn completed and the response was POSTed back via the Telegram Bot API.
If the bot doesn’t reply, jump to Common failures. Don’t install as a service until this works; installing a broken bridge just moves the failure to a less inspectable place.
Step 6: install as a service
Once the foreground proves the wiring is sound, register the gateway as a background service:
hermes gateway install
| OS | What install writes |
|---|---|
| Linux (systemd) | ~/.config/systemd/user/hermes-gateway.service |
| macOS (launchd) | ~/Library/LaunchAgents/ai.hermes.gateway.plist |
| WSL2 | Same as Linux, but systemd=true must be set in /etc/wsl.conf first or the unit silently fails to start. |
| Docker | Service does not apply. Run gateway run under s6 or docker-compose with restart: unless-stopped. |
| Native Windows | Task Scheduler task. Hermes recommends WSL2 over native Windows for anything gateway-shaped. |
After install, enable linger on Linux hosts you SSH into (sudo loginctl enable-linger $USER), or the user-scoped service dies the moment your SSH session closes. hermes gateway status reports running only when the service is up and the adapter has a healthy getUpdates connection; if it stays disconnected, restart and check $HERMES_HOME/logs/gateway.log. hermes pairing approve telegram XKGH5N7P is the alternative to a hard-coded allow-list — a stranger who DMs the bot gets a one-time pairing code, you approve it, they are in.
Common failures
| Symptom | First check | Fix |
|---|---|---|
| Bot never responds to a DM | hermes gateway status --deep | Your numeric user ID is not in allowed_users. Get it from @userinfobot, add it via hermes gateway setup. |
| Bot answers in DMs only | hermes config get gateway.platforms.telegram.allowed_chats | Add the group ID (negative for supergroups) to allowed_chats. |
401 Unauthorized on first message | Token rotation | /revoke then /token in BotFather; paste the new token into hermes gateway setup. |
Gateway shows disconnected | hermes gateway status --deep and journalctl --user -u hermes-gateway -n 50 | Restart the service; most often a stale PID in ~/.local/state/hermes/gateway-locks/ from an earlier crashed run. |
| Bot answers once, then goes silent | Gateway log + cron log | A long agent turn triggered a stall. /stop from the chat, then hermes gateway restart. |
TELEGRAM_BOT_TOKEN empty in cron delivery | grep TELEGRAM /opt/data/.env | Same token file the gateway uses; cron reads from .env too. |
clock skew or repeated reconnects | chronyc tracking (or w32tm /query /status on Windows) | Bot API requires time within ~30s. Fix NTP first, then restart the gateway. |
| ”Bot token already in use (PID NNNN)” where NNNN is a defunct process | ps -o pid,stat -p NNNN | A previous gateway child died uncleanly; the lock-holder is a zombie. kill NNNN does nothing on zombies; clean up with hermes gateway uninstall && hermes gateway install, then restart. |
The structural pattern: check the allow-list before changing the token, and check the service state before reading logs. Most “the bot is broken” reports are one of those two.
Verification checklist
Run these in order. Each row proves one layer.
| # | Command | What it proves |
|---|---|---|
| 1 | hermes gateway list | Telegram adapter is registered. |
| 2 | hermes gateway setup | Token and allow-list are saved. |
| 3 | hermes gateway status | Adapter reports healthy at idle. |
| 4 | hermes gateway run --platform telegram | Foreground bridge connects (Telegram long-poll goes green). |
| 5 | Send a Telegram message | Reply within seconds = inbound + agent + outbound all proven. |
| 6 | hermes gateway install | Service registered for autostart. |
| 7 | hermes gateway status after reboot | Service stayed up across the reboot. |
Sources
- Hermes messaging docs
- Telegram BotFather
- ABS companion: Hermes Setup v2
- ABS companion: Hermes Agent Gateway: When Messages Don’t Arrive
Last verified: 2026-07-14 against Hermes v0.18.2 and gateway/platforms/telegram.py. Token is a secret — never paste it into a chat, an issue tracker, or git history; on leak, /revoke and hermes gateway setup.


Submit a take
Have a different read on this? Drop a comment below — your email isn't published, and I read every one. Nothing leaves the site until I approve it.