Deploy AI agents to Telegram, Discord, Slack, and WhatsApp
Messaging Bots enable your AI agents to interact with users across Telegram, Discord, Slack, WhatsApp, and Linear.
The same agent can serve one unified conversation per human across all platforms with Cross-Platform Sessions — message from Telegram in the morning, Discord at noon, and keep the same conversation history.
Start a bot with a single command - no Python code required:
1
Set Environment Variables
# Telegramexport TELEGRAM_BOT_TOKEN="123456:ABC-DEF..."# Discordexport DISCORD_BOT_TOKEN="MTIz..."# Slack (requires both tokens)export SLACK_BOT_TOKEN="xoxb-..."export SLACK_APP_TOKEN="xapp-..."# WhatsApp (Cloud API)export WHATSAPP_ACCESS_TOKEN="EAAx..."export WHATSAPP_PHONE_NUMBER_ID="123456789"export WHATSAPP_VERIFY_TOKEN="my-secret-verify"# Linear (OAuth token or API key)export LINEAR_OAUTH_TOKEN="your-linear-oauth-token"export LINEAR_WEBHOOK_SECRET="your-webhook-secret"
2
Start the Bot
# Telegrampraisonai bot telegram --token $TELEGRAM_BOT_TOKEN# Discordpraisonai bot discord --token $DISCORD_BOT_TOKEN# Slackpraisonai bot slack --token $SLACK_BOT_TOKEN --app-token $SLACK_APP_TOKEN# WhatsApp Cloud API (starts a webhook server on port 8080)praisonai bot whatsapp --token $WHATSAPP_ACCESS_TOKEN --phone-id $WHATSAPP_PHONE_NUMBER_ID# WhatsApp Web Mode (no tokens needed — scan QR code)praisonai bot whatsapp --mode web# Linear (AgentSession webhooks)praisonai bot linear --token $LINEAR_OAUTH_TOKEN --signing-secret $LINEAR_WEBHOOK_SECRET# Email Botpraisonai bot email --address bot@gmail.com --password "xxxx xxxx xxxx xxxx"# AgentMail Bot (API-first, no IMAP/SMTP)praisonai bot agentmail --token $AGENTMAIL_API_KEY
A default agent is created automatically with basic assistant capabilities.
3
(Optional) Custom Agent
# Use a custom agent configurationpraisonai bot slack --token $SLACK_BOT_TOKEN --app-token $SLACK_APP_TOKEN --agent agents.yaml
agents.yaml:
name: support-botinstructions: | You are a customer support assistant. Be helpful and concise.llm: gpt-4o-minitools: - search_web
Full programmatic control with Python:
1
Create Agent
from praisonaiagents import Agentagent = Agent( name="assistant", instructions="Help users with their questions", llm="gpt-4o-mini")
2
Configure Bot
from praisonaiagents import BotConfigconfig = BotConfig( token="your-bot-token", command_prefix="/", typing_indicator=True, # renews every 4s reply_in_thread=False, # Inline replies)
3
Start Bot
from praisonai.bots import SlackBot # or TelegramBot, DiscordBotbot = SlackBot( token="xoxb-...", app_token="xapp-...", agent=agent, config=config)# Run the botimport asyncioasyncio.run(bot.start())
4
Linear Bot Example
from praisonai.bots import LinearBotbot = LinearBot( token="your-linear-oauth-token", signing_secret="your-webhook-secret", agent=agent, webhook_port=8080)asyncio.run(bot.start())
Outbound tools are from praisonai-tools for sending messages from agents.
For receiving messages via bot runtime, use the CLI or Python SDK shown above.Install: pip install praisonai-tools
When bots are connected through PraisonAI UI (praisonai chat), channel messages appear in the Chat dashboard with full visibility into agent processing steps — tool calls, reasoning, and intermediate responses stream in real-time.
All channel messages and responses are persisted and replayable
The streaming bridge hooks into the same StreamEventEmitter used by the web chat, so channel messages get identical step visibility as messages typed directly in the dashboard.
Bots ship with sensible defaults so you can start chatting immediately — no tool wiring required.Both praisonai bot start and praisonai gateway start apply the same defaults:
Set tools: [] explicitly in YAML, or pass tools=[] to Agent
Require manual approval
Set auto_approve_tools: false in the channel config
Override the tool list
Set default_tools: [...] under the channel — destructive tools are still filtered
Destructive tools (execute_command, delete_file, write_file, shell_command) are never auto-injected, even if you add them to default_tools. Wire them explicitly on the agent and add a chat-level approval flow.
Upgrading from an older release? auto_approve_tools used to default to False. If your bot relied on manual approval, set auto_approve_tools: false explicitly.
from praisonaiagents import Agentfrom praisonai.bots import Bot# Zero config — gets search_web, schedule_*, memory, learning, and auto-approvalagent = Agent(name="assistant", instructions="Help the user")bot = Bot("telegram", agent=agent)bot.run()
# Same result — no YAML tool list neededpraisonai bot telegram --token $TELEGRAM_BOT_TOKEN
Socket Mode works by opening an outbound WebSocket connection to Slack/Discord. No public URL or port forwarding is needed - your bot initiates the connection from behind NAT/firewall.
from praisonaiagents import BotConfigconfig = BotConfig( token="bot-token", # Bot authentication token webhook_url=None, # Webhook URL (optional) command_prefix="/", # Command prefix mention_required=True, # Require @mention in groups typing_indicator=True, # Show typing indicator max_message_length=4096, # Max message length allowed_users=[], # Empty list → unknown_user_policy decides (default deny) allowed_channels=[], # Allowed channel IDs reply_in_thread=False, # Reply in threads (default: inline) thread_threshold=500, # Auto-thread if response > N chars (0 = disabled) group_policy="mention_only", # Group chat policy: respond_all, mention_only, command_only auto_approve_tools=True, # Auto-approve safe tool executions (default: True for chat bots) unknown_user_policy="pair", # "deny" (default) | "pair" | "allow" owner_user_id="123456789", # Platform-specific owner ID (Telegram numeric, Discord snowflake, Slack U-id))
Option
Type
Default
Description
token
str
""
Bot authentication token
webhook_url
str
None
Webhook URL for webhook mode
command_prefix
str
"/"
Prefix for bot commands
mention_required
bool
True
Require @mention in channels (DMs never require mention)
typing_indicator
bool
True
Show typing indicator. On Telegram, the indicator is renewed every 4 seconds for the entire duration of the agent run so users keep seeing “typing…” even during 20–30s RAG / tool-call operations.
max_message_length
int
4096
Max message length
allowed_users
list
[]
Allowed user IDs. Empty list now defers to unknown_user_policy (deny / pair / allow). Earlier releases treated empty as “allow everyone” on Telegram — fixed in PR #1885.
allowed_channels
list
[]
Allowed channel IDs
timeout
int
30
Request timeout
reply_in_thread
bool
False
Always reply in threads
thread_threshold
int
500
Auto-thread responses longer than N chars (0 = disabled)
group_policy
str
"mention_only"
Group chat behavior: respond_all, mention_only, or command_only
Safe tools auto-injected when the agent has no tools configured. Destructive tools (execute_command, delete_file, write_file, shell_command) are excluded from auto-injection even if listed.
auto_approve_tools
bool
True
Skip confirmation for safe tool execution. Chat bots can’t show CLI approval prompts, so this defaults to True. Set False to require approval (only useful if you wire a chat-level approval flow).
unknown_user_policy
Literal["deny","pair","allow"]
"deny"
How to handle messages from users not in allowed_users. deny silently drops, pair runs the owner-approval flow, allow lets everyone through. With an empty allowed_users, every user is “unknown” and this policy applies to all of them.
owner_user_id
Optional[str]
None
Platform-specific ID of the bot owner. Required for inline-button approvals — without it, "pair" policy falls back to a plain-text CLI instruction.
retry_attempts
int
3
Number of retry attempts for failed operations
polling_interval
float
1.0
Interval for polling mode (seconds)
Reply behavior:
Default: Inline replies in the channel
Auto-thread: Responses > 500 chars are automatically threaded
Force thread: Set reply_in_thread=True to always use threads
Group policy:
mention_only — Bot only responds when @mentioned (default, safest)
respond_all — Bot responds to every message in the group
Create a Verify Token (any secret string you choose)
The temporary access token from the API Setup page expires in 24 hours. For production, generate a System User Token from Business Settings → System Users.
3
Configure Webhook
Go to Configuration → Webhook
Click Edit and enter:
Callback URL: https://your-domain.com/webhook
Verify Token: The same string you set in WHATSAPP_VERIFY_TOKEN
Subscribe to: messages
For local development, use ngrok to create a public HTTPS URL:
Send a WhatsApp message to your business phone number. The bot will reply via the Cloud API.
Troubleshooting: Webhook not verifying
Verify token matches? → Must be identical in Meta console and env var
Bot running? → Webhook server must be up before configuring in Meta
HTTPS required? → Meta only accepts HTTPS webhook URLs
ngrok running? → If using ngrok, ensure the tunnel is active
WhatsApp API Limits
Tier
Messages/day
Requirement
Test
1,000
Default for new apps
Business
10,000+
Verified business account
Unlimited
No limit
Meta approval required
Experimental Feature — WhatsApp Web mode uses a reverse-engineered protocol (not officially supported by Meta). Your WhatsApp number may be banned. Use Cloud API mode for production workloads.
WhatsApp Web mode connects directly via the WhatsApp Web protocol — no tokens, no Meta developer account, no webhooks needed. Just scan a QR code.
1
Install Dependencies
pip install 'praisonai[bot-whatsapp-web]'
This installs neonize (WhatsApp Web client with built-in QR display).
2
Start the Bot
praisonai bot whatsapp --mode web
A QR code appears in your terminal. Open WhatsApp on your phone → Settings → Linked Devices → Link a Device → scan the QR code.
3
Send a Message
Send any message to the linked WhatsApp number from another phone. The bot will reply using your AI agent.Your session is saved locally — on restart, the bot reconnects automatically without scanning again.
CLI
Python
YAML
# Basic web modepraisonai bot whatsapp --mode web# With custom credentials directorypraisonai bot whatsapp --mode web --creds-dir ~/.myapp/wa-creds# With agent capabilitiespraisonai bot whatsapp --mode web --agent agents.yaml --memory --web
from praisonaiagents import Agentfrom praisonai.bots import WhatsAppBotagent = Agent(name="assistant", instructions="Be helpful")# Web mode — no tokens neededbot = WhatsAppBot(mode="web", agent=agent)import asyncioasyncio.run(bot.start()) # Shows QR → scan → running
# whatsapp-web-bot.yamlplatform: whatsappmode: webagent: name: "WhatsApp Assistant" instructions: "You are a helpful AI assistant." llm: "gpt-4o-mini" memory: true
Channel bots can show live draft replies that update in real-time as your agent thinks, replacing the old “stare at typing indicator for 45 seconds” experience. Currently available on Telegram with other platforms coming soon.
from praisonai.bots import TelegramBot, StreamingConfig, StreamingModebot = TelegramBot(token="...", agent=agent)bot.configure_streaming(StreamingConfig(mode=StreamingMode.DRAFT))bot.start()
Three modes are available:
off (default) — Single final message after completion
draft — Progressive edits with growing answer text
progress — Show tool status, then replace with final answer
Bot Streaming Replies
Complete guide to streaming reply configuration and best practices
from praisonaiagents import BotConfigconfig = BotConfig( token="your-token", allowed_users=["user123", "user456"], allowed_channels=["channel789"])
from praisonaiagents import BotConfigconfig = BotConfig( token="your-token", webhook_url="https://your-domain.com/webhook", webhook_path="/telegram/webhook")
from praisonaiagents import BotConfigconfig = BotConfig( token="your-token", mention_required=True, # Only respond when @mentioned command_prefix="!", # Use ! for commands)
The Slack bot uses Slack Bolt, Slack’s official Python framework.
When running, you’ll see “⚡️ Bolt app is running!” - this confirms the bot is connected and listening.
By default, WhatsApp Web mode responds only to self-chat messages — when you message your own number. This prevents the bot from replying to every conversation on your account, including messages you send in other people’s chats.
Self-chat means messaging your own phone number — not just any message you send. Messages you send in other people’s chats or groups are filtered out by default.
Phone numbers are normalized automatically — +1-234-567-890, 1234567890, and (123) 456-7890 all match the same number. Group IDs use WhatsApp’s JID format (e.g., 120363123456@g.us).Stale message guard: Messages older than when the bot connected are always dropped, even with --respond-to-all. This prevents replaying old conversations on reconnect.
Run all bots simultaneously with a single gateway config:gateway.yaml:
agents: personal: name: "Personal Assistant" instructions: "You are a helpful personal assistant." llm: "gpt-4o-mini" support: name: "Support Agent" instructions: "You are a customer support agent." llm: "gpt-4o-mini"channels: telegram: token: "${TELEGRAM_BOT_TOKEN}" routing: dm: "personal" group: "support" default: "personal" discord: token: "${DISCORD_BOT_TOKEN}" routing: dm: "personal" channel: "support" default: "support" slack: token: "${SLACK_BOT_TOKEN}" app_token: "${SLACK_APP_TOKEN}" auto_approve_tools: true # NEW (default: true for chat bots) # default_tools: # NEW — override the safe tool list per channel # - search_web # - store_memory routing: dm: "personal" channel: "support" default: "support" whatsapp: token: "${WHATSAPP_ACCESS_TOKEN}" phone_number_id: "${WHATSAPP_PHONE_NUMBER_ID}" verify_token: "${WHATSAPP_VERIFY_TOKEN}" webhook_port: 8080 routing: dm: "personal" default: "personal" # Or use WhatsApp Web mode (no tokens needed): # whatsapp: # mode: web # routing: # dm: "personal" # default: "personal"
Channel key vs platform — in gateway.yaml, the YAML key under channels: (e.g. telegram, telegram_cfo) is just an identifier you choose. When the key is not a platform name, set platform: telegram (or whichever) inside the block so the gateway can pick the right protocol. This enables running multiple bots on the same platform. Note: bot.yaml requires channel keys to be platform names.
Behind the scenes the gateway calls Agent.clone_for_channel() on the configured agent for each channel, so per-channel tools or routing rules never leak between bots. See Agent Cloning.
praisonai gateway --config gateway.yaml
The gateway now produces identical results to Bot() — agents get the same safe tools and auto-approval in both entry points.
The gateway uses routing rules to send messages to different agents based on context (DM vs group vs channel). Each channel can have its own routing configuration.
Run a bot with a single YAML file — no Python code needed:bot.yaml:
platform: telegramtoken: "${TELEGRAM_BOT_TOKEN}"agent: name: "My Assistant" instructions: "You are a helpful AI assistant." llm: "gpt-4o-mini" memory: true web_search: true tools: [search_web]
praisonai bot start --config bot.yaml
Tip: You can omit tools: entirely — the bot auto-injects safe defaults (search_web, schedule, memory, learning). Keep tools: [] only if you want the bot to run with zero tools.
The .env file in the current directory is auto-loaded, so you can store tokens there and reference them with ${VAR_NAME} syntax.
Never commit bot tokens to version control. Use environment variables or secure secret management.
Use allowlists in production
Set allowed_users and allowed_channels to prevent unauthorized access to your bot.
Enable mention requirement for groups
Set mention_required=True to prevent the bot from responding to every message in group chats.
Handle rate limits gracefully
Configure retry_attempts and implement exponential backoff for API rate limits.
Safe default tools only
The default tool list is intentionally safe (search_web, schedule_*, memory/learning). Tools like execute_command require explicit opt-in and should be paired with an approval backend. See Approval Protocol.
Set approval flow for auto-approve disabled
Only set auto_approve_tools: false if you’ve wired a chat-level approval flow (e.g. SlackApproval). Otherwise tool calls will hang silently waiting for a CLI prompt the user cannot see.
You can also define multiple agents in an agents.yaml file for complex workflows:agents.yaml:
agents: searcher: name: Researcher role: Web Researcher goal: Search the web for relevant information on the given topic instructions: | Search the web thoroughly for information on the user's query. Return comprehensive, accurate results with sources. tools: - search_web summarizer: name: Summarizer role: Content Summarizer goal: Create clear, concise summaries of information instructions: | Take the research findings and create a well-structured summary. Highlight key points and insights. Keep it concise but informative.
When users send multiple rapid messages (e.g. “hey” → “can you” → “search for AI news”), debounce coalesces them into a single agent call — saving tokens and preventing duplicate responses.
Long agent responses are split at paragraph boundaries while preserving code fences — no more broken code blocks mid-message.
How splitting works
1
Paragraph boundaries
Split at blank lines (\n\n) first — keeps ideas together.
2
Sentence boundaries
If a paragraph is still too long, split at sentence endings (. ).
3
Hard split
Last resort: character-level split for very long single lines.
Code fence protection
Code blocks wrapped in triple backticks are never split, even if they exceed max_message_length. This ensures your users always see complete, copyable code.
Smart chunking is enabled automatically for all bot adapters. No configuration needed.
Override max_message_length in BotConfig to change the split threshold (default: 4096).
Bot agents automatically remember the last 20 messages per conversation — no extra dependencies required.
Automatic (default)
Custom memory
from praisonaiagents import Agentfrom praisonai.bots import Bot# History is injected automatically — zero config neededagent = Agent(name="assistant", instructions="Be helpful")bot = Bot("telegram", agent=agent)bot.run()# Agent now remembers last 20 messages per session
from praisonaiagents import Agentfrom praisonai.bots import Bot# Override with your own memory configagent = Agent( name="assistant", instructions="Be helpful", memory={"history": True, "history_limit": 50} # Remember more)bot = Bot("telegram", agent=agent)bot.run()
Setting memory=True (full memory with ChromaDB) requires pip install praisonaiagents[memory].
The default history=True injection uses zero extra dependencies.
Acknowledge inbound messages with an emoji reaction (e.g. ⏳) so users know the bot is processing, then swap to a done emoji (e.g. ✅) when the response is sent.
Onboard-generated configs enable ⏳ / ✅ by default. Set ack_emoji: "" to opt out.
Currently wired for Telegram (which supports native message reactions).
RAG-enabled agents commonly take 20–30 seconds to answer (vector search + tool calls + LLM). PraisonAI Telegram bots give users continuous feedback throughout, with no extra code on your side.
from praisonaiagents import Agentfrom praisonaiagents.bots import BotConfigfrom praisonai.bots import Botagent = Agent(name="cfo", instructions="Answer finance questions using the company knowledge base.")# Defaults shown for clarity — you get all this without setting anything.bot = Bot("telegram", agent=agent, config=BotConfig( typing_indicator=True, # renews every 4s ack_emoji="⏳", # instant reaction at T+0 done_emoji="✅", # swaps in when the reply lands))bot.run()
Deploy bots entirely from YAML — including agent memory, tools, roles, and multi-platform config.
Full YAML
Python loader
agent: name: Research Bot role: AI Research Assistant goal: Help users find and understand AI research instructions: | You are a research assistant that helps users find and understand the latest AI research papers. llm: gpt-4o-mini memory: history: true history_limit: 30 tools: - search_webplatforms: telegram: token: ${TELEGRAM_BOT_TOKEN} debounce_ms: 1500 discord: token: ${DISCORD_BOT_TOKEN} slack: token: ${SLACK_BOT_TOKEN}
from praisonai.bots import BotOSbotos = BotOS.from_config("bot.yaml")botos.run() # Starts all platforms concurrently
Supported agent fields
Field
Type
Description
name
string
Agent display name
role
string
Role/job title
goal
string
Primary objective
backstory
string
Background context
instructions
string
Direct instructions
llm
string
Model name (gpt-4o-mini, claude-3-sonnet)
memory
bool/dict
Memory config (true or {history: true})
tools
list
Tool names from praisonaiagents.tools
knowledge
list
Knowledge sources
guardrail
string
Output validation
Environment variable syntax
Use ${ENV_VAR} in YAML values — resolved at load time.
When your bot agent uses dangerous tools (e.g. execute_command), you can route approval requests to Slack:
from praisonaiagents import Agentfrom praisonai.bots import Bot, SlackApprovalagent = Agent( name="assistant", instructions="You are a helpful assistant.", approval=SlackApproval(channel="#approvals") # Approvals go to Slack)bot = Bot("telegram", agent=agent) # Users talk via Telegrambot.run()