Bot platform adapters now ship in the
praisonai-bot package. praisonai bot serve still works exactly as documented here; for a standalone install see praisonai-bot Migration.Quick Start
How It Works
The pairing system intercepts messages from users not inallowed_users and routes them through an approval workflow controlled by the unknown_user_policy, publishing a pairing_approved event on the default EventBus after every successful approval (CLI, inline button, or web UI).
Inline approvals (PR #1892): When the owner taps Approve on the Telegram / Discord / Slack DM, the requester is paired immediately on the platform type (
telegram / discord / slack) so all of their later messages flow straight through. If you saw “approve looked successful but the next message was still blocked” on an older release, upgrade to pick up this fix.Gateway mode (
praisonai gateway start): As of PR #1791, the gateway polling path runs the same pairing pipeline as the standalone bot. If pairing.enabled: true is set in your gateway config, unknown users get the same Approve/Deny inline-button flow. No code changes needed — configure unknown_user_policy: "pair" and owner_user_id in your channels: YAML entry.Policy Options
| Policy | Behaviour | When to use |
|---|---|---|
"deny" (default) | Silently drops messages from users not in allowed_users. | Closed / internal bots. |
"pair" | Generates a code and DMs the owner an Approve/Deny button. Falls back to CLI if owner_user_id is unset. | Semi-public bots where you want owner control. |
"allow" | Lets every unknown user through (no persistent pair). | Fully public bots (combine with rate limits / approval protocol). |
Which Policy Should I Choose?
CLI Fallback
Whenowner_user_id is not set, the bot replies to the requester:
<channel_type> is one of: telegram, discord, slack.
Security: HMAC-signed Callbacks
Internals (PR #2108): Pairing approve/deny now dispatches through the generic
InteractiveRegistry under the reserved pair namespace. The behavior, callback format, and HMAC verification are identical — but you can now register your own handlers under different namespaces (e.g. approval, menu) on the same adapter without conflicting. See Interactive Bot Actions.- Callback format:
pair:{action}:{channel}:{user_id}:{code}:{sig} - Signature: First 8 hex chars of
HMAC-SHA256(PRAISONAI_CALLBACK_SECRET, payload) - Verification: Tampered
callback_datafails verification and is silently ignored + logged
Platform-specific UI
Telegram
Telegram
Uses Implementation: Telegram’s
InlineKeyboardMarkup with ✅ Approve / ❌ Deny buttons. Callbacks are handled via CallbackQueryHandler that parses the signed callback_data and verifies the HMAC signature.What the owner sees:InlineKeyboardButton with callback_data containing the signed pairing payload. The {channel} field in the pair:{action}:{channel}:{user_id}:{code}:{sig} format contains the platform identifier (telegram), not the chat ID.Discord
Discord
Uses Implementation: Discord’s Button components in an Action Row with HMAC-signed
discord.ui.View with success (green) and danger (red) button styles. Handled via button.callback method that verifies the HMAC signature in the custom_id.What the owner sees:custom_id values. The {channel} field in the callback format is the platform identifier (discord).Slack
Slack
Uses Block Kit Implementation: Slack Block Kit buttons with HMAC-signed values and dedicated action handlers. The
actions block with primary (blue) and danger (red) button styles. Handled via @app.action("pair_approve") and @app.action("pair_deny") decorators that verify the signature in the button’s value.What the owner sees:{channel} field in the callback format is the platform identifier (slack).Configuration Options
For the completeBotConfig options including unknown_user_policy and owner_user_id, see the canonical reference at Messaging Bots Configuration.
Common Patterns
- Semi-public Bot
- Internal Bot
- Public Bot
- Gateway YAML
Best Practices
Always set PRAISONAI_CALLBACK_SECRET in production
Always set PRAISONAI_CALLBACK_SECRET in production
Generate a strong secret and set it as an environment variable:Without this, inline buttons stop working when your bot restarts.
Use platform-native user IDs for owner_user_id
Use platform-native user IDs for owner_user_id
Each platform has its own user ID format:
- Telegram: Numeric ID (e.g.,
123456789) - Discord: Snowflake ID (e.g.,
123456789012345678) - Slack: User ID format (e.g.,
U1234ABCD)
Combine 'allow' policy with rate limiting and tool approval
Combine 'allow' policy with rate limiting and tool approval
If using Consider also implementing rate limiting at the platform level.
unknown_user_policy="allow" for a public bot, protect yourself with:Treat denied pairings as final
Treat denied pairings as final
When you deny a pairing request, the code is consumed and cannot be retried. The user must send a new message to generate a fresh code. This prevents spam and ensures each approval decision is deliberate.
Pair a user with a non-empty
--label (the canonical id) and StoreBackedIdentityResolver automatically unifies their session across every channel paired under the same label. No separate praisonai identity link calls needed. See Cross-Platform Sessions › StoreBackedIdentityResolver.Related
Messaging Bots
Complete bot configuration and setup
Bot Security
Security best practices for bots
Web-UI HTTP API Approval
HTTP API endpoints for web admin UI approval
Cross-Platform Sessions
Unified per-user sessions across platforms

