Documentation Index Fetch the complete documentation index at: https://docs.praison.ai/llms.txt
Use this file to discover all available pages before exploring further.
Bot pairing lets unknown users self-request access to your bot with secure 8-character codes that you approve from the CLI.
Quick Start
Enable Pairing Policy
from praisonaiagents import Agent
from praisonaiagents . bots import BotConfig
config = BotConfig (
allowed_users =[ " @owner " ],
unknown_user_policy = " pair " , # Enable pairing for unknown users
)
# Bot setup with Telegram adapter (other platforms pending)
# from praisonai.bots.telegram import TelegramAdapter
# bot = TelegramAdapter(config)
Approve Pairing Requests
# List pending pairing requests
praisonai pairing list
# Approve a user's pairing code
praisonai pairing approve telegram ABCD1234 --label " alice "
# View all paired channels
praisonai pairing list
How It Works
Policy Configuration
Unknown User Policies
from praisonaiagents . bots import BotConfig
# Policy options
config = BotConfig (
allowed_users =[ " @owner " , " 123456789 " ],
unknown_user_policy = " pair " # Choose one below
)
Policy Behavior Use Case "deny"Silently drop messages (default) Private bots, testing "allow"Allow all users (overrides allowed_users) Public bots "pair"Use pairing flow for approval Controlled access
Pairing Rate Limiting
The system includes built-in protection against code generation spam:
Rate Limit : 10 minutes between code generations per channel
Code TTL : Codes expire after a configurable time (default: check PaisingStore implementation)
Automatic Cleanup : Stale rate limit entries are automatically evicted
CLI Commands
List Commands
# List all paired channels
praisonai pairing list
# Example output:
# Found 3 paired channels:
#
# Platform: telegram
# Channel: @alice_username
# Paired: 2026-04-22 10:30:15
# Label: alice
#
# Platform: telegram
# Channel: 987654321
# Paired: 2026-04-22 11:45:22
# Label: bob
Approve Command
# Approve with auto-resolved channel ID (when code is bound)
praisonai pairing approve telegram ABCD1234
# Approve with explicit channel ID
praisonai pairing approve telegram ABCD1234 987654321
# Approve with human-readable label
praisonai pairing approve telegram ABCD1234 --label " alice "
# Use custom store directory
praisonai pairing approve telegram ABCD1234 --store-dir /path/to/store
Revoke Access
# Revoke specific channel
praisonai pairing revoke telegram 987654321
# Clear all pairings (with confirmation)
praisonai pairing clear
# Are you sure you want to clear ALL paired channels? [y/N]: y
# ✅ Cleared 3 paired channels
# Clear without confirmation prompt
praisonai pairing clear --confirm
Web-UI / HTTP API Approval
Approve, list, and revoke pairings from a web admin UI via the gateway’s HTTP API.
Endpoints
List Pending
Approve Pairing
Revoke Access
GET /api/pairing/pending Returns pending pairing requests awaiting approval. curl -X GET " http://localhost:8000/api/pairing/pending " \
-H " Cookie: session=<admin-session-cookie> "
import httpx
resp = httpx . get (
" http://localhost:8000/api/pairing/pending " ,
cookies ={ " session " : " <admin-session-cookie> " },
)
resp . raise_for_status ()
print ( resp . json ()) # {"pending": [{"channel_type": "ui", "code": "ABCD1234", "channel_id": "user123", "age_seconds": 45}]}
POST /api/pairing/approve Approve a pairing code to authorize the channel. curl -X POST " http://localhost:8000/api/pairing/approve " \
-H " Content-Type: application/json " \
-H " Cookie: session=<admin-session-cookie> " \
-d ' {"channel": "ui", "code": "ABCD1234"} '
import httpx
# Admin operator approves a pending UI pairing
resp = httpx . post (
" http://localhost:8000/api/pairing/approve " ,
json ={ " channel " : " ui " , " code " : " ABCD1234 " },
cookies ={ " session " : " <admin-session-cookie> " },
)
resp . raise_for_status ()
print ( resp . json ()) # {"approved": True, "channel": "ui", "code": "ABCD1234"}
POST /api/pairing/revoke Revoke authorization for a previously paired channel. curl -X POST " http://localhost:8000/api/pairing/revoke " \
-H " Content-Type: application/json " \
-H " Cookie: session=<admin-session-cookie> " \
-d ' {"channel": "ui", "user_id": "user123"} '
import httpx
resp = httpx . post (
" http://localhost:8000/api/pairing/revoke " ,
json ={ " channel " : " ui " , " user_id " : " user123 " },
cookies ={ " session " : " <admin-session-cookie> " },
)
resp . raise_for_status ()
print ( resp . json ())
All three routes are gated by an admin auth checker (auth_admin) wired in by create_pairing_routes(pairing_store, auth_admin). Non-admin sessions receive 403. Invalid codes return 404.
The code parameter on /approve is consumed atomically — replays return 404.
Reacting to Pairing Approvals (EventBus)
Subscribe to the pairing_approved event to run agent logic the moment a channel is approved — for example, send a welcome DM, log to your CRM, or warm a per-user memory store.
from praisonaiagents import Agent
from praisonaiagents . bus import get_default_bus
welcomer = Agent (
name = " Welcomer " ,
instructions = " Write a one-line friendly welcome message for a new user. " ,
)
def on_pairing_approved ( event ):
channel = event . data [ " channel " ]
code = event . data [ " code " ]
welcome = welcomer . start ( f "Welcome the new { channel } user with code { code } " )
print ( welcome )
get_default_bus (). subscribe ( on_pairing_approved , [ " pairing_approved " ])
Event Payload
Field Type Description event.typestrAlways "pairing_approved" event.data["channel"]strChannel type (e.g., "telegram", "ui", "slack") event.data["code"]strThe pairing code that was approved
Handlers run synchronously in the request path — keep them fast or hand off to a background queue.
Current Implementation
Platform Status Handler Wiring CLI Support Telegram ✅ Shipped ✅ Complete ✅ Full Web UI (HTTP API) ✅ Shipped ✅ via /api/pairing/* routes ✅ Full Discord 🔧 Pending ❌ Not wired ✅ CLI ready Slack 🔧 Pending ❌ Not wired ✅ CLI ready WhatsApp 🔧 Pending ❌ Not wired ✅ CLI ready
Platform Implementation Status : PR #1504 ships the pairing system and CLI with full Telegram support. Other platform adapters need handler wiring to complete the integration.
Telegram Integration
# Telegram adapter includes UnknownUserHandler wiring
from praisonai . bots . telegram import TelegramAdapter
from praisonaiagents . bots import BotConfig
config = BotConfig (
token = os . getenv ( " TELEGRAM_BOT_TOKEN " ),
unknown_user_policy = " pair "
)
# Handler automatically wired in Telegram adapter
bot = TelegramAdapter ( config )
Security Model
Code Generation
8-character codes : Hex format (e.g., ABCD1234)
HMAC signatures : Codes are cryptographically signed
Per-install secret : Auto-generated if PRAISONAI_GATEWAY_SECRET unset
Channel binding : Codes can be bound to specific channel IDs
Secret Management
# Option 1: Set explicit gateway secret (recommended for production)
export PRAISONAI_GATEWAY_SECRET = " your-secure-secret-key "
# Option 2: Auto-generated per-install secret
# Stored at <store_dir>/.gateway_secret with 0600 permissions
# Persists across restarts, unique per installation
Secret Persistence : Without PRAISONAI_GATEWAY_SECRET, a per-install secret is auto-generated and stored at <store_dir>/.gateway_secret with mode 0600. This file is critical for code verification across restarts.
Security Features
Rate Limiting : 600s (10 min) window per channel prevents spam
HMAC Verification : Codes are cryptographically signed and verified
TTL Expiration : Codes automatically expire after configured time
Atomic Operations : Pairing state persisted atomically to disk
User Interaction Flow
Step-by-Step Process
Unknown User DMs Bot
Bot Generates Pairing Code
Bot: Your pairing code: `ABCD1234`
Owner: `praisonai pairing approve telegram ABCD1234`
Owner Approves via CLI
$ praisonai pairing approve telegram ABCD1234 --label " alice "
✅ Successfully paired telegram channel 987654321
Label: alice
User Can Now Interact Normally
Unknown User: Hello!
Bot: Hi there! How can I help you today?
Rate Limit Handling
If a user tries to generate codes too frequently:
User: Hello!
Bot: [no response - rate limited]
# In logs:
# DEBUG: Rate limited channel 987654321 (last code: 120.5s ago)
The user must wait for the rate limit window (10 minutes) to expire before requesting a new code.
Configuration Options
Store Directory
# Default: ~/.praisonai/pairing/
praisonai pairing list
# Custom directory
praisonai pairing list --store-dir /custom/path
# All commands support --store-dir
praisonai pairing approve telegram ABCD1234 --store-dir /custom/path
Environment Variables
# Explicit gateway secret (recommended for production)
export PRAISONAI_GATEWAY_SECRET = " your-256-bit-secret "
# Custom store directory (optional)
export PRAISONAI_STORE_DIR = " /custom/store/path "
Best Practices
Production Secret Management
Set PRAISONAI_GATEWAY_SECRET explicitly in production environments to ensure consistent code verification across deployments. # Generate secure secret
openssl rand -hex 32 > gateway_secret.txt
# Set in production
export PRAISONAI_GATEWAY_SECRET =$( cat gateway_secret.txt )
Watch for rate limiting warnings in logs - they indicate potential pairing spam or legitimate users hitting limits. # Look for these log patterns:
# DEBUG: Rate limited channel 123456 (last code: 45.2s ago)
# INFO: Generated pairing code for user123 on telegram: ABCD1234
Add labels when approving pairings to identify channels later. # Good - easy to identify
praisonai pairing approve telegram ABCD1234 --label " alice-work "
praisonai pairing approve telegram EFGH5678 --label " bob-personal "
# Less helpful
praisonai pairing approve telegram ABCD1234
Periodically review paired channels and revoke access for inactive users. # Review all pairings
praisonai pairing list
# Revoke specific channels
praisonai pairing revoke telegram 987654321
# Clear all if starting fresh
praisonai pairing clear --confirm
Bot Security Comprehensive bot security and DM policies
Messaging Bots Bot platform setup and configuration