Skip to main content

Telegram Integration

This guide walks through connecting an AI agent to Telegram via the Pai Provider system. There are two patterns:

  • Interactive bot — a persistent service agent that polls getUpdates and responds to user messages in real time
  • Notification agent — a task agent (one-shot or scheduled) that sends reports to a chat via sendMessage

How it works

Telegram integration uses the Provider Proxy sidecar to intercept all calls to api.telegram.org and inject the real bot token transparently:

Agent Container
| TELEGRAM_BOT_TOKEN=pai-managed (dummy value)
| (DNS: api.telegram.org → 127.0.0.1)
v
Sidecar Proxy
|── rewrites /bot<dummy>/<method> → /bot<real-token>/<method>
|── logs API call for audit
v
api.telegram.org (real endpoint)

The agent never holds the real bot token. The sidecar rewrites the URL path on every request before forwarding to Telegram.

Step 1: Create a Telegram bot

  1. Open Telegram and search for @BotFather
  2. Send /newbot
  3. Choose a name for your bot (e.g., "My Pai Agent")
  4. Choose a username (must end in bot, e.g., my_pai_agent_bot)
  5. BotFather will respond with your bot token — a string like 7123456789:AAH1bGc...

Save this token securely. You will need it in the next step.

Step 2: Store the bot token as a secret

pai add secret telegram-token --from-literal token=7123456789:AAH1bGcXYZ...

Step 3: Create the Provider

apiVersion: pai.io/v1
kind: Provider
metadata:
name: telegram-bot
spec:
type: telegram
auth:
type: token
secretRef: telegram-token
secretKey: token
policy:
allow: ["*"]
audit:
logRequests: true

Apply it:

pai apply -f telegram-provider.yaml

Key fields explained:

FieldPurpose
type: telegramActivates the Telegram provider plugin in the sidecar
auth.type: tokenSingle-token credential — the sidecar injects it by rewriting the URL path
auth.secretRefKubernetes Secret holding the real bot token
policy.allow: ["*"]Allow all Telegram API methods

The sidecar automatically injects TELEGRAM_BOT_TOKEN=pai-managed into the agent container. The agent uses this as a placeholder; the sidecar replaces it with the real token in the URL before forwarding.


Pattern A: Interactive bot (service agent)

A persistent agent that stays running, polls getUpdates, and responds to users in Telegram.

apiVersion: pai.io/v1
kind: Agent
metadata:
name: my-telegram-bot
spec:
type: service
image: ghcr.io/pai-platform/openclaw:latest
runAsUser: 1000
models:
- anthropic/claude-sonnet-4-6
providers:
- telegram-bot
inbound:
port: 3000
configFiles:
- path: /home/node/.openclaw/openclaw.json
content: |
{
"channels": ["telegram"],
"defaultModel": "claude-sonnet"
}

Deploy and verify:

pai apply -f my-telegram-bot.yaml
pai status my-telegram-bot
# Providers:
# - telegram-bot (telegram / api.telegram.org) — 12 requests today

Pattern B: Scheduled notification agent (task agent)

A task agent that runs on a schedule, researches or processes data, and posts a formatted message to a specific Telegram chat. No custom container image needed — uses the Pai harness.

Agent

apiVersion: pai.io/v1
kind: Agent
metadata:
name: daily-briefing
spec:
models:
- anthropic/claude-sonnet-4-6
providers:
- telegram-bot
tokens:
maxPerDay: 2000000
maxPerRequest: 16000
system: |
You are a daily briefing agent. Research today's highlights and send
a formatted report to Telegram.

Use web_search and web_fetch (max_bytes=15000 per page, at most 6 URLs)
to gather information, then send via bash:

curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-H "Content-Type: application/json" \
-d "{\"chat_id\": \"${TELEGRAM_CHAT_ID}\", \"text\": \"...\", \"parse_mode\": \"Markdown\"}"

Telegram constraints:
- Max 4096 characters per message; use multiple sendMessage calls if needed
- Use Markdown parse_mode
tools:
- type: web_search
- type: web_fetch
- type: bash
env:
- name: TELEGRAM_CHAT_ID
value: "YOUR_CHAT_ID" # replace with your Telegram user or group ID
resources:
requests:
cpu: "250m"
memory: "256Mi"
limits:
cpu: "1"
memory: "512Mi"

Scheduled Agent

apiVersion: pai.io/v1
kind: Agent
metadata:
name: daily-briefing-job
spec:
type: task
agentDefinition: daily-briefing
schedule: "0 9 * * *" # Every day at 9:00am UTC
title: >
Research today's highlights and send a formatted report to Telegram.

Apply both:

pai apply -f template-agent.yaml
pai apply -f agent.yaml

kubectl get agent daily-briefing-job
# NAME TYPE PHASE SCHEDULE LAST RUN AGE
# daily-briefing-job task Scheduled 0 9 * * * Complete 1d

Finding your chat ID

To get your Telegram user ID or a group's chat ID:

  1. Send a message to your bot
  2. Open https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates in a browser
  3. Look for "chat":{"id":123456789} — that number is your chat ID

Or use @userinfobot to get your own user ID instantly.

Triggering a run immediately

# Run once right now, outside the schedule
kubectl create job --from=cronjob/pai-cron-daily-briefing-job manual-$(date +%s) -n pai-system

Policy examples

Allow all Telegram methods (most common for notification agents):

policy:
allow: ["*"]

Restrict an interactive bot to safe read/send methods only:

policy:
allow:
- messages:send
- updates:read
- callback:answer
- chat:read
- bot:read
deny:
- chat:manage # block ban/unban/pin actions

Security notes

  • The real bot token is stored in a Kubernetes Secret and mounted only in the sidecar container
  • The agent container receives TELEGRAM_BOT_TOKEN=pai-managed — the sidecar rewrites the URL on every outbound call before forwarding to Telegram
  • All Telegram API calls are logged in the audit trail with timestamps and method names
  • Policy rules let you allow only specific Telegram methods per Provider

Troubleshooting

"Unauthorized" errors in agent logs

The token may have been rotated. Update the secret and restart:

pai delete secret telegram-token
pai add secret telegram-token --from-literal token=NEW_TOKEN_HERE
# Restart the agent (service) or re-run the task

Scheduled task not firing

Check that the CronJob exists and is not suspended:

kubectl get cronjob -n pai-system
kubectl describe cronjob pai-cron-daily-briefing-job -n pai-system

To resume a suspended CronJob:

kubectl patch cronjob pai-cron-daily-briefing-job -n pai-system -p '{"spec":{"suspend":false}}'