> ## Documentation Index
> Fetch the complete documentation index at: https://opensre.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Telegram

> Deliver investigation findings to a Telegram chat or channel.

OpenSRE's Telegram integration delivers investigation findings to any chat your bot has been added to — useful for mobile-first on-call rotations and personal alerting.

Start the interactive shell with `opensre` (no subcommand). Slash commands below are run from that REPL.

***

## Prerequisites

* A Telegram account.
* The Telegram mobile or desktop app, signed in.
* The chat (group, channel, or direct message) where you want to receive findings.

***

## Step 1: Create a bot with BotFather

[BotFather](https://t.me/BotFather) is Telegram's official bot for creating other bots.

1. Open Telegram and search for `@BotFather`. Open the chat and tap **Start**.
2. Send `/newbot`.
3. When prompted, send a **display name** for your bot (e.g. `OpenSRE Alerts`).
4. Send a **username** that ends in `bot` (e.g. `opensre_alerts_bot`). It must be globally unique.
5. BotFather replies with an **HTTP API token** of the form `<numeric-id>:<token-secret>`. Copy it — it is your **bot token**. Treat it like a password. Anyone holding it can send messages as your bot.

<Tip>
  You can change the bot name, picture, and description later by sending `/mybots` to BotFather and selecting your bot.
</Tip>

***

## Step 2: Add the bot to a chat

The bot can deliver to three kinds of destinations. Pick the one that fits your team:

<Tabs>
  <Tab title="Group chat">
    1. Open the group where you want findings to land.
    2. Tap the group name → **Add members** → search for your bot username → **Add**.
    3. By default, bots in groups only see messages addressed to them, which is fine for delivery-only.
  </Tab>

  <Tab title="Channel">
    1. Open your channel and tap its name.
    2. Tap **Administrators → Add Administrator**, search for your bot, and add it.
    3. Grant the **Post Messages** permission. No other admin permissions are required.
  </Tab>

  <Tab title="Direct message">
    Open a chat with your bot directly (search its username) and send `/start`. The bot will not reply, but Telegram registers the chat so the bot can message you.
  </Tab>
</Tabs>

***

## Step 3: Find your `chat_id`

The **chat ID** identifies where the bot should post. It is required — without it
the bot has nowhere to send anything.

<Tip>
  **Posting to a public channel?** Skip this step — setup accepts the channel's
  `@name` (for example `@acme_alerts`) directly. Private groups and DMs have no
  `@name`, so they need the steps below.
</Tip>

1. Send any message in the destination chat — for a channel, post anything; for a DM, send `/start` to your bot.

2. In a browser, open:

   ```
   https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates
   ```

   (replace `<YOUR_BOT_TOKEN>` with the token from Step 1)

3. In the JSON response, look for a `chat.id` field. The value depends on the chat type:

   | Chat type                  | Format                                | Example          |
   | -------------------------- | ------------------------------------- | ---------------- |
   | Direct message with a user | Positive integer                      | `123456789`      |
   | Group                      | Negative integer                      | `-987654321`     |
   | Large group or channel     | Negative integer starting with `-100` | `-1001234567890` |

   Copy the entire value, **including the leading minus sign** for groups and channels.

<Note>
  If `getUpdates` returns an empty array, post a fresh message in the chat and reload — Telegram only buffers recent updates.
</Note>

***

## Step 4: Configure the integration

### Option A: Onboarding wizard (recommended)

Interactive shell:

```text theme={null}
/onboard
```

CLI:

```bash theme={null}
opensre onboard
```

Choose **Telegram** from the integration list. The wizard prompts for:

* **Bot token** (required) — stored in the system keyring (not plain `.env`)
* **Default chat ID or `@channelname`** (required) — written to `.env` as
  `TELEGRAM_DEFAULT_CHAT_ID`

Credentials are also saved to `~/.opensre/integrations.json` via `upsert_integration("telegram", ...)`.

Both answers are checked before anything is saved, so a wrong token or a chat the
bot was never added to fails here rather than silently at the first alert.

<Note>
  Setup confirms the bot can *see* the chat, not that it may post there. For a
  channel the bot still needs the **Post Messages** permission from Step 2.
</Note>

Non-interactive setup:

Interactive shell:

```text theme={null}
/integrations setup telegram
```

CLI:

```bash theme={null}
opensre integrations setup telegram
```

### Option B: Environment variables

Set in `.env` (bot token can also live in the keyring after wizard setup):

```bash theme={null}
TELEGRAM_BOT_TOKEN=<numeric-id>:<token-secret>
TELEGRAM_DEFAULT_CHAT_ID=<chat-id>
```

| Variable                   | Description                                                  |
| -------------------------- | ------------------------------------------------------------ |
| `TELEGRAM_BOT_TOKEN`       | Bot HTTP API token from BotFather. Required.                 |
| `TELEGRAM_DEFAULT_CHAT_ID` | Default delivery destination. Required for delivery to work. |

OpenSRE picks these up at startup and registers Telegram as an active integration.

<Note>
  **Credential resolution.** Every Telegram delivery surface — investigations,
  **background RCA completion notifications**, the scheduler, `/watchdog`,
  `/hermes watch`, and `/watch` — resolves the bot token the same way: integration
  store first, then `resolve_env_credential("TELEGRAM_BOT_TOKEN")` (process env,
  then OS keyring). Chat id is non-secret: `--chat-id` → store `default_chat_id` →
  `TELEGRAM_DEFAULT_CHAT_ID` env (plain `os.getenv`, never keyring). Either setup
  option above works for all of them.
</Note>

***

## Step 5: Verify

Interactive shell:

```text theme={null}
/integrations verify telegram
```

Or:

```text theme={null}
/verify telegram
```

CLI:

```bash theme={null}
opensre integrations verify telegram
```

This calls Telegram's [`getMe`](https://core.telegram.org/bots/api#getme) endpoint. On success it reports the bot `@username`. On failure it reports the Telegram API error message verbatim.

<Note>
  **Verify only checks that your bot token is valid.** It does not start a listening process and does not test two-way communication. If you DM your bot at this point, it will appear online but not reply. To receive and respond to messages from Telegram, complete Step 6 below.
</Note>

You can also trigger a real delivery test against a bundled fixture:

Interactive shell:

```text theme={null}
/investigate tests/e2e/kubernetes/fixtures/datadog_k8s_alert.json
```

CLI:

```bash theme={null}
opensre investigate --input tests/e2e/kubernetes/fixtures/datadog_k8s_alert.json
```

Findings should appear in the configured chat. Long reports are truncated to Telegram's 4,096-character message limit.

***

## Step 6: Enable two-way chat (DM the agent from Telegram)

> **Skip this step if you only need outbound delivery** (alerts, cron reports, investigation findings posted to a chat). Steps 1–5 are sufficient for that.

After verify succeeds, you have outbound delivery but the bot will not reply to your messages yet. Two extra steps are required:

### 6a — Allow your Telegram user

Find your **numeric user id** with [@userinfobot](https://t.me/userinfobot), then:

Interactive shell:

```text theme={null}
/messaging allow -p telegram -u 123456789
```

CLI:

```bash theme={null}
opensre messaging allow -p telegram -u 123456789
```

Or set in `.env`:

```bash theme={null}
TELEGRAM_ALLOWED_USERS=123456789   # comma-separated for multiple users
```

<Warning>
  Use the **numeric user id** (Telegram's `from.id`), not a `@username` and not
  the bot handle. Inbound authorization only ever matches the numeric id, so a
  `@handle` produces an allow-list entry that can never match a real sender — you
  will see `User <id> is not in the allowed users list` for every message. The CLI
  now rejects non-numeric Telegram ids, but older entries may still be wrong; check
  with `/messaging status -p telegram` (or `opensre messaging status -p telegram`)
  and re-add with the numeric id.
</Warning>

### 6b — Start the gateway daemon

```bash theme={null}
opensre gateway start
```

This runs the background daemon that listens for inbound Telegram DMs and routes them to the OpenSRE agent. Once it is running, DM your bot from Telegram — use `/new` for a fresh session or `/help` for built-in commands.

Useful follow-up commands:

| Command                   | What it does                    |
| ------------------------- | ------------------------------- |
| `opensre gateway status`  | Show daemon and component state |
| `opensre gateway logs -f` | Follow live gateway logs        |
| `opensre gateway stop`    | Stop the daemon                 |

<Note>
  The gateway uses long polling — no public HTTPS URL or port forwarding is required for local use. See the [Two-way chat gateway](#two-way-chat-gateway-dm-text) section below for deployment and remote-host setup.
</Note>

***

## Programmatic messaging: `telegram_send_message` tool

When the Telegram integration is configured, OpenSRE exposes a
`telegram_send_message` tool. The tool can send user-requested action messages,
incident notifications, or follow-up updates to the configured default chat, or
to an explicit `chat_id` when one is supplied. Ask in plain language from the
interactive shell (for example: "send a Telegram message to the team that DB CPU
is back below 70%").

```json theme={null}
{
  "name": "telegram_send_message",
  "arguments": {
    "chat_id": "-1001234567890",
    "message": "DB CPU is back below 70%; keeping the incident open for 10 minutes."
  }
}
```

The tool resolves the bot token from the same credential chain as the watchdog:
integration store, then `resolve_env_credential("TELEGRAM_BOT_TOKEN")` (env then keyring).
If `chat_id` is omitted, it sends through the configured `default_chat_id`, so a
user can ask OpenSRE to send a message through the configured Telegram bot
without knowing or exposing the bot token.

Delivery is an external side effect. Its result includes a stable `status`,
`sent`, `error_type`, `chat_id`, `reply_to_message_id`, and `message_length`
shape so follow-up tool calls can tell configuration failures from Telegram
delivery failures.

***

## Two-way chat gateway (DM text)

OpenSRE can also run a **Telegram messaging gateway** so you can chat with the agent from your phone in a private DM. v1 supports **text-only direct messages** (no groups, voice, or attachments).

### Allow your Telegram user

Find your **numeric user id** with [@userinfobot](https://t.me/userinfobot), then:

Interactive shell:

```text theme={null}
/messaging allow -p telegram -u 123456789
```

CLI:

```bash theme={null}
opensre messaging allow -p telegram -u 123456789
```

or set `TELEGRAM_ALLOWED_USERS=123456789` in `.env`.

<Warning>
  Use the **numeric user id** (Telegram's `from.id`), not a `@username` and not
  the bot handle. Inbound authorization only ever matches the numeric id, so a
  `@handle` produces an allow-list entry that can never match a real sender — you
  will see `User <id> is not in the allowed users list` for every message. The CLI
  now rejects non-numeric Telegram ids, but older entries may still be wrong; check
  with `/messaging status -p telegram` (or `opensre messaging status -p telegram`)
  and re-add with the numeric id.
</Warning>

### DM pairing (optional)

Same policy as other messaging platforms. Generate a code:

Interactive shell:

```text theme={null}
/messaging pair -p telegram
```

CLI:

```bash theme={null}
opensre messaging pair -p telegram
```

Then open Telegram, DM your bot, and send:

```text theme={null}
/pair <code>
```

Check pairing status:

Interactive shell:

```text theme={null}
/messaging status -p telegram
```

CLI:

```bash theme={null}
opensre messaging status -p telegram
```

Revoke access for a user:

Interactive shell:

```text theme={null}
/messaging revoke -p telegram -u 123456789
```

CLI:

```bash theme={null}
opensre messaging revoke -p telegram -u 123456789
```

### Long polling (local and production)

No public HTTPS URL required. The gateway is OpenSRE's **background agent daemon** —
one process that runs the Telegram chat worker, the web health app, and the
cron task scheduler. It runs in the background by default:

| Command                            | What it does                                          |
| ---------------------------------- | ----------------------------------------------------- |
| `opensre gateway start`            | Start the daemon (web, Telegram chat, task scheduler) |
| `opensre gateway start -f`         | Run attached to the terminal instead                  |
| `opensre gateway status`           | Show the daemon and each component's state            |
| `opensre gateway logs [-n N] [-f]` | Print (or follow) the daemon logs                     |
| `opensre gateway stop`             | Stop the daemon                                       |

```bash theme={null}
export TELEGRAM_BOT_TOKEN=<your-bot-token>
uv run opensre gateway start
```

Starting prints the log location; logs are stored in `~/.opensre/gateway/gateway.log`.
If Telegram is not configured the daemon still runs the other components and
`opensre gateway status` shows `telegram: not configured`.

The same controls are available inside the interactive shell via `/gateway`:

```text theme={null}
/gateway start     # start the background daemon
/gateway status    # daemon + per-component state
/gateway logs 50   # last 50 log lines
/gateway stop      # stop it
```

Send a DM to your bot. Use `/new` to start a fresh session; `/help` for built-in commands.

The gateway uses the same headless agent harness as the interactive shell, with Telegram-specific wiring:

* **Prompt grounding** — CLI reference, AGENTS.md, integration list, and investigation flow context
* **Read-only evidence tools** — live integration queries (GitHub, logs, metrics, etc.) via the gather pass when integrations are configured
* **Action tools** — `shell_run` and `investigation_start`

Investigation delivery, background RCA completion notifications, watchdog alerts, and cron still use the outbound-only paths documented above — they do not require the gateway process.

### Deploying the gateway to a remote host

Running the gateway on a server with `make deploy-gateway` (EC2) is different from local
use in one important way: **the remote host cannot read your local machine's
keychain**. Guided setup stores the bot token (and your LLM API key) in the system
keyring, which is perfect locally but does not travel to the deployed instance.

So `make deploy-gateway` validates that the required secrets are present as **plaintext
env vars** in `.env`, and aborts if they are only in the keychain:

```bash theme={null}
TELEGRAM_BOT_TOKEN=<numeric-id>:<token-secret>
TELEGRAM_ALLOWED_USERS=123456789        # numeric user id(s), comma-separated
ANTHROPIC_API_KEY=...                    # or your provider's key env
```

If `make deploy-gateway` reports `MISSING: TELEGRAM_BOT_TOKEN — API key not set` or
`MISSING: OPENAI_API_KEY — API key not set` (or your provider's key env) even
though local setup succeeded, this is
why — copy the values into `.env` (or `.env.deploy.example`) for the deploy. A
"missing" here means "not in the deploy env", not "not configured".

***

## Scheduled, background, and watchdog delivery

### Background investigation completion (RCA)

When the interactive shell runs investigations in background mode, Telegram can deliver the RCA summary as soon as a job finishes. Configure Telegram first (Steps 4–5 — the gateway in Step 6 is **not** required), then in the shell:

```text theme={null}
/background on
/background notify set telegram          # or: email,telegram
/investigate datadog
```

Keep using the shell while the RCA runs. On completion the summary — root cause, top analysis, next steps, and a short stats block — is posted as plain text to your default chat, truncated to Telegram's 4,096-character limit.

Confirm delivery with `/background show <task_id>`: the `notify` row reads `telegram:sent`, `telegram:failed: <error>`, or `telegram:missing telegram integration: …` when the bot token or chat id is not configured. A notification problem never fails the investigation itself.

See [Background investigations](/docs/background-investigations) for the full command set.

### Cron (recurring reports)

Interactive shell:

```text theme={null}
/cron add --kind daily_summary --cron "0 9 * * 1-5" --tz Asia/Kolkata --provider telegram --chat-id <chat_id>
/cron list
/cron run <task_id>
/cron logs <task_id>
```

CLI:

```bash theme={null}
opensre cron add --kind daily_summary --cron "0 9 * * 1-5" \
  --tz Asia/Kolkata --provider telegram --chat-id <chat_id>
opensre cron list
opensre cron run <task_id>
opensre cron logs <task_id>
```

See [Cron](/docs/cron) for task kinds and scheduler daemon setup.

### Watchdog (process threshold alarms)

Configure Telegram first (Steps 4–5), then:

Interactive shell:

```text theme={null}
/watch <pid> [--max-cpu N] [--max-runtime D] [--max-rss S] [--cooldown D] [--interval N] [--once]
/watches
/unwatch <task_id>
```

Or:

```text theme={null}
/watchdog --pid <pid> [--max-cpu N] [--max-runtime D] [--max-rss S]
```

CLI:

```bash theme={null}
opensre watchdog --pid <pid> [--max-cpu N] [--max-runtime D] [--max-rss S]
```

### Hermes incident escalation

Interactive shell:

```text theme={null}
/hermes watch
```

CLI:

```bash theme={null}
opensre hermes watch
```

See [Hermes](/docs/hermes) for log tailing and incident classification setup.

***

## Troubleshooting

`/integrations verify telegram`, `/verify telegram`, and `opensre integrations verify telegram` only call Telegram's `getMe` endpoint, so they surface token-validity errors but cannot detect chat-routing problems. Delivery-time errors only show up when an investigation actually posts.

### Errors from verify

**`Missing bot_token`**

`TELEGRAM_BOT_TOKEN` is empty. Re-check `.env` and restart any long-running OpenSRE process so it re-reads the file.

**`Telegram API check failed: 401 Client Error: Unauthorized for url: …`**

The bot token is invalid or has been revoked. Generate a new one in BotFather (`/mybots → your bot → API Token → Revoke current token`) and update `.env`. The `for url:` portion of the message shows the token as `<redacted>` — Telegram's API endpoint embeds the token in the path, so the verifier scrubs it before surfacing the error.

### Errors that only surface during delivery

These are Telegram API responses that come back when OpenSRE actually tries to post a finding. `verify` only calls `getMe`, so it cannot catch them. They appear in OpenSRE logs as `[telegram] post message failed: <description>` with the Telegram description copied verbatim.

**`description: chat not found`**

The bot is not in the chat, or `TELEGRAM_DEFAULT_CHAT_ID` is wrong. Re-add the bot and re-fetch `chat_id` from `getUpdates`.

**`description: bot was kicked from the large group …` (or similar)**

Re-add the bot. For channels, the bot must be an administrator with **Post Messages** permission.

**Findings never arrive, but `verify` passes**

`getMe` only confirms the token is valid; it does not test delivery. Send a fresh message in the destination chat and re-fetch `chat_id` from `getUpdates` — your `chat_id` may have changed (for example, if a group was upgraded and now uses a `-100` prefix).

**Gateway: `User <id> is not in the allowed users list`**

Run `/messaging status -p telegram` and confirm the sender's numeric user id is listed. Re-add with `/messaging allow -p telegram -u <user_id>` or complete `/messaging pair -p telegram` pairing.
