You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When an unknown user DMs a PraisonAI bot (Telegram / Discord / Slack), the system can now:
Deliver a pairing code to the bot owner's DM with inline Approve / Deny buttons
Approve the user for future conversations with one tap
Fall back to a plain-text code + CLI instruction when owner_user_id is not configured
Verify every button callback with an HMAC signature so payloads can't be tampered with
This is a user-facing feature with concrete new config fields, a new runtime workflow, and a new CLI fallback — none of which is currently documented. This issue tracks the documentation work.
Decision: update + new content
After reviewing the docs repo, both a content update and a new page are required:
Action
Path
Reason
UPDATE
docs/features/messaging-bots.mdx
The canonical BotConfig reference is here. Two new fields (unknown_user_policy semantics expanded + new owner_user_id) need to be added to the options example and the options table.
NEW PAGE
docs/features/bot-unknown-user-pairing.mdx
No existing page covers the unknown-user workflow, inline buttons, HMAC signing, or CLI fallback. Warrants a dedicated agent-centric feature page.
UPDATE
docs/best-practices/bot-security.mdx
Currently labels pairing as "planned functionality". That warning must be removed and the section rewritten against the shipped implementation.
UPDATE
docs.json
Register the new page under the Features → Communication & Messaging group (right after docs/features/bot-routing).
Placement rules compliance (from AGENTS.md §1.8): Place the new page in docs/features/ — NOTdocs/concepts/. Concepts is human-approved only.
SDK Source of Truth (PR #1518)
Read these files before writing any doc content. Paths are relative to the PraisonAI repo root:
Integration tests (useful for verifying docs examples)
Note: The PraisonAIDocs repo mirrors these via the daily update_repos.sh sync. Until the next sync copies them into praisonaiagents/bots/ and praisonai/bots/ at the repo root, verify directly in the PraisonAI repo at the paths above.
Ground-truth: Config Fields (verbatim from SDK)
From praisonaiagents/bots/config.py (after PR #1518):
# Unknown user policy: "deny" (default), "pair", or "allow"unknown_user_policy: UnknownUserPolicy="deny"# Owner user ID for pairing approvals (platform-specific format)owner_user_id: Optional[str] =Nonedef__post_init__(self) ->None:
ifself.unknown_user_policynotin {"deny", "pair", "allow"}:
raiseValueError(
f"unknown_user_policy must be one of: deny, pair, allow. Got: {self.unknown_user_policy}"
)
From praisonaiagents/bots/pairing_types.py (new file):
PRAISONAI_CALLBACK_SECRET — HMAC secret used to sign/verify inline-button callback payloads. Defined in praisonai/bots/_pairing_ui.py::_get_callback_secret():
If set in env, that value is used.
If unset, a random per-process secrets.token_hex(32) is generated — callbacks stop working across restarts.
Must be set in production.
Behavioural flow (document this verbatim on the new page)
Unknown DM
→ UnknownUserHandler.handle()
→ PairingStore.is_paired(user_id, channel_type)? YES → allow message through
→ policy == "allow" → allow (no persistent pair)
→ policy == "deny" → silently drop
→ policy == "pair":
→ PairingStore.generate_code(channel_type)
→ owner_user_id set + adapter?
→ adapter.send_approval_dm(owner, user_name, code, channel, user_id)
→ Builds platform-specific inline buttons (HMAC-signed callback_data)
→ Notifies requester: "Your request has been sent to the owner for approval."
→ Owner taps Approve/Deny
→ callback_data = "pair:{action}:{channel}:{user_id}:{code}:{sig}"
→ PairingCallbackHandler.parse_and_verify_callback() checks HMAC
→ action == "approve": PairingStore.verify_and_pair(...) → notify requester "You've been approved!"
→ action == "deny": consume code, reply "❌ Denied"
→ else: CLI fallback
→ reply to requester: "Your pairing code: {code}. Ask the owner to run: praisonai pairing approve {channel_type} {code}"
Required doc changes — detail
1. UPDATE docs/features/messaging-bots.mdx
Current file (lines 337–369) documents BotConfig options but is missing the two new fields. Add them to both the example block and the options table.
In the example config block (around line 337–352):
In the options table (around line 354–369), add these two rows immediately after auto_approve_tools:
Option
Type
Default
Description
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.
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.
Add a short cross-reference <Note> linking to the new dedicated page:
<Note>
For the full owner-approval workflow (inline buttons on Telegram / Discord / Slack, HMAC signing, CLI fallback), see [Bot Unknown-User Pairing](/docs/features/bot-unknown-user-pairing).
</Note>
2. NEW page docs/features/bot-unknown-user-pairing.mdx
Follow the AGENTS.md template exactly. Required sections:
Frontmatter
---
title: "Bot Unknown-User Pairing"sidebarTitle: "Unknown-User Pairing"description: "Owner-DM inline-button approval for unknown users on Telegram, Discord, and Slack bots"icon: "user-check"
---
Step 1 – Simplest: enable pairing with two fields.
frompraisonaiagentsimportAgentfrompraisonaiagents.botsimportBotConfigconfig=BotConfig(
token="YOUR_BOT_TOKEN",
unknown_user_policy="pair",
owner_user_id="123456789", # your Telegram/Discord/Slack user ID
)
agent=Agent(
name="Support",
instructions="You are a helpful support assistant.",
)
# ... wire config to your bot (telegram/discord/slack) as usual
Step 2 – Production env: set PRAISONAI_CALLBACK_SECRET.
Explain in one sentence that without this, inline-button callbacks stop working across restarts.
How It Works (sequenceDiagram)
sequenceDiagram
participant User as Unknown User
participant Bot
participant Store as PairingStore
participant Owner
User->>Bot: DM
Bot->>Store: is_paired? → No
Bot->>Store: generate_code()
Bot->>Owner: DM with Approve / Deny buttons
Bot-->>User: "Your request has been sent..."
Owner->>Bot: Taps Approve (HMAC-signed callback)
Bot->>Store: verify_and_pair()
Bot-->>User: "You've been approved! Send me a message."
User->>Bot: Future DMs flow straight through
Loading
Policies table
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).
Add a small mermaid decision diagram ("which policy should I choose?") — AGENTS.md §6.1 requires this when a page offers multiple options.
CLI fallback
When owner_user_id is not set, the bot replies to the requester:
Your pairing code: abc12345. Ask the owner to run: praisonai pairing approve telegram abc12345
Document the exact command: praisonai pairing approve <channel_type> <code> where <channel_type> ∈ telegram | discord | slack.
Security: HMAC-signed callbacks
Explain in 2–3 sentences (non-developer friendly):
Callback data format: pair:{action}:{channel}:{user_id}:{code}:{sig}
sig = first 8 hex chars of HMAC-SHA256(PRAISONAI_CALLBACK_SECRET, "pair:{action}:{channel}:{user_id}:{code}")
A tampered callback_data fails verification and is silently ignored + logged.
Include a <Warning> that without PRAISONAI_CALLBACK_SECRET set in env, a random per-process secret is used and inline buttons stop working after restart.
Platform-specific UI (one accordion per platform)
For each, show the on-screen rendering in words plus the underlying mechanism from the SDK:
Telegram — InlineKeyboardMarkup with ✅ Approve / ❌ Deny; callback via CallbackQueryHandler.
Discord — discord.ui.View + success/danger Buttons; handled via button.callback.
Slack — Block Kit actions block with primary/danger buttons; handled via @app.action("pair_approve") / @app.action("pair_deny").
Configuration Options section
Link to the canonical table on messaging-bots.mdx (do not duplicate — AGENTS.md §2 "Configuration Options"). Also add Card links to the auto-generated SDK refs if/when they exist.
Common Patterns
Semi-public bot with approval gate (unknown_user_policy="pair" + owner_user_id set)
Fully-open public bot ("allow" + rate_limit / approval_protocol)
Best Practices (<AccordionGroup>)
Always set PRAISONAI_CALLBACK_SECRET in production.
Use platform-native user IDs for owner_user_id (Telegram numeric, Discord snowflake, Slack Uxxxxx).
Combine "allow" with rate limiting and tool approval.
Treat a denied pairing as final — the code is consumed so it can't be retried.
Related (<CardGroup cols={2}>)
docs/features/messaging-bots
docs/best-practices/bot-security
3. UPDATE docs/best-practices/bot-security.mdx
The current file (lines 217–265) says:
Note: The pairing system described below represents planned functionality. Current SDK implementation may differ. Verify against actual SDK documentation.
This is now incorrect — the pairing system is shipped.
Required changes:
Remove the <Warning> that pairing is "planned".
Replace the /pair abc12345-style user-side example with the new owner-DM flow (Approve / Deny buttons) as the primary pattern.
Keep the CLI praisonai pairing approve … flow as the documented fallback, not the main flow.
Update the "Gateway Pairing" heading and intro to reflect that unknown_user_policy="pair" is now the entry point, with owner_user_id being the key new config.
AddPRAISONAI_CALLBACK_SECRET to the "Self-Hoster Security Checklist → Gateway Pairing Active" accordion (in addition to the existing PRAISONAI_GATEWAY_SECRET).
4. UPDATE docs.json
Under the Features → Communication & Messaging group (currently at lines 281–294 of docs.json), add the new page right after bot-routing:
Code examples must run unmodified — verify imports against the PraisonAI repo.
Do not duplicate the BotConfig options table on the new page. Link to messaging-bots.mdx.
Do not touchdocs/concepts/ — those are human-approved only.
Acceptance criteria
docs/features/messaging-bots.mdx lists unknown_user_policy and owner_user_id in both the example code block and the options table, with a <Note> linking to the new page.
docs/features/bot-unknown-user-pairing.mdx exists with: frontmatter, hero mermaid, Quick Start (<Steps>), How It Works sequenceDiagram, Policies table + decision mermaid, CLI fallback, HMAC-signing security note + PRAISONAI_CALLBACK_SECRET warning, per-platform accordion, Common Patterns, Best Practices (<AccordionGroup>), Related (<CardGroup>).
docs/best-practices/bot-security.mdx no longer describes pairing as "planned"; examples reflect the owner-DM button flow and add PRAISONAI_CALLBACK_SECRET to the checklist.
docs.json registers docs/features/bot-unknown-user-pairing under "Communication & Messaging" and remains valid JSON.
Every code example uses from praisonaiagents... imports that actually resolve against PR #1518.
Context
Upstream PR MervinPraison/PraisonAI#1518 was merged on 2026-04-22. It ships the owner-DM inline-button pairing approval system for messaging bots (originally tracked as issue MervinPraison/PraisonAI#1511).
When an unknown user DMs a PraisonAI bot (Telegram / Discord / Slack), the system can now:
owner_user_idis not configuredThis is a user-facing feature with concrete new config fields, a new runtime workflow, and a new CLI fallback — none of which is currently documented. This issue tracks the documentation work.
Decision: update + new content
After reviewing the docs repo, both a content update and a new page are required:
docs/features/messaging-bots.mdxBotConfigreference is here. Two new fields (unknown_user_policysemantics expanded + newowner_user_id) need to be added to the options example and the options table.docs/features/bot-unknown-user-pairing.mdxdocs/best-practices/bot-security.mdxdocs.jsondocs/features/bot-routing).SDK Source of Truth (PR #1518)
Read these files before writing any doc content. Paths are relative to the PraisonAI repo root:
src/praisonai-agents/praisonaiagents/bots/config.pyBotConfigdataclass with the two new fieldssrc/praisonai-agents/praisonaiagents/bots/pairing_types.pyUnknownUserPolicy,PairingReply,PairingApprovalResulttypes (new file)src/praisonai/praisonai/bots/_unknown_user.pyUnknownUserHandler,BotContext,BotAdapterprotocol (new file)src/praisonai/praisonai/bots/_pairing_ui.pyPairingUIBuilder,PairingCallbackHandler, HMAC signing (new file)src/praisonai/praisonai/bots/telegram.pyCallbackQueryHandlersrc/praisonai/praisonai/bots/discord.pydiscord.ui.Buttonsrc/praisonai/praisonai/bots/slack.py@app.action("pair_approve")/"pair_deny"src/praisonai/tests/integration/bots/test_pairing_owner_dm.pyGround-truth: Config Fields (verbatim from SDK)
From
praisonaiagents/bots/config.py(after PR #1518):From
praisonaiagents/bots/pairing_types.py(new file):Runtime environment variable
PRAISONAI_CALLBACK_SECRET— HMAC secret used to sign/verify inline-button callback payloads. Defined inpraisonai/bots/_pairing_ui.py::_get_callback_secret():secrets.token_hex(32)is generated — callbacks stop working across restarts.Behavioural flow (document this verbatim on the new page)
Required doc changes — detail
1. UPDATE
docs/features/messaging-bots.mdxCurrent file (lines 337–369) documents
BotConfigoptions but is missing the two new fields. Add them to both the example block and the options table.In the example config block (around line 337–352):
In the options table (around line 354–369), add these two rows immediately after
auto_approve_tools:unknown_user_policyLiteral["deny","pair","allow"]"deny"allowed_users.denysilently drops,pairruns the owner-approval flow,allowlets everyone through.owner_user_idOptional[str]None"pair"policy falls back to a plain-text CLI instruction.Add a short cross-reference
<Note>linking to the new dedicated page:2. NEW page
docs/features/bot-unknown-user-pairing.mdxFollow the AGENTS.md template exactly. Required sections:
Frontmatter
Hero mermaid (graph LR, standard colour scheme)
Show: Unknown DM → Bot → Owner DM (inline buttons) → Approve/Deny → User gets access.
Quick Start (
<Steps>)Step 1 – Simplest: enable pairing with two fields.
Step 2 – Production env: set
PRAISONAI_CALLBACK_SECRET.Explain in one sentence that without this, inline-button callbacks stop working across restarts.
How It Works (sequenceDiagram)
sequenceDiagram participant User as Unknown User participant Bot participant Store as PairingStore participant Owner User->>Bot: DM Bot->>Store: is_paired? → No Bot->>Store: generate_code() Bot->>Owner: DM with Approve / Deny buttons Bot-->>User: "Your request has been sent..." Owner->>Bot: Taps Approve (HMAC-signed callback) Bot->>Store: verify_and_pair() Bot-->>User: "You've been approved! Send me a message." User->>Bot: Future DMs flow straight throughPolicies table
"deny"(default)allowed_users."pair"owner_user_idis unset."allow"Add a small mermaid decision diagram ("which policy should I choose?") — AGENTS.md §6.1 requires this when a page offers multiple options.
CLI fallback
When
owner_user_idis not set, the bot replies to the requester:Document the exact command:
praisonai pairing approve <channel_type> <code>where<channel_type>∈telegram | discord | slack.Security: HMAC-signed callbacks
Explain in 2–3 sentences (non-developer friendly):
pair:{action}:{channel}:{user_id}:{code}:{sig}sig= first 8 hex chars ofHMAC-SHA256(PRAISONAI_CALLBACK_SECRET, "pair:{action}:{channel}:{user_id}:{code}")callback_datafails verification and is silently ignored + logged.Include a
<Warning>that withoutPRAISONAI_CALLBACK_SECRETset in env, a random per-process secret is used and inline buttons stop working after restart.Platform-specific UI (one accordion per platform)
For each, show the on-screen rendering in words plus the underlying mechanism from the SDK:
InlineKeyboardMarkupwith ✅ Approve / ❌ Deny; callback viaCallbackQueryHandler.discord.ui.View+ success/dangerButtons; handled viabutton.callback.actionsblock with primary/danger buttons; handled via@app.action("pair_approve")/@app.action("pair_deny").Configuration Options section
Link to the canonical table on
messaging-bots.mdx(do not duplicate — AGENTS.md §2 "Configuration Options"). Also add Card links to the auto-generated SDK refs if/when they exist.Common Patterns
unknown_user_policy="pair"+owner_user_idset)"deny"+ explicitallowed_userslist)"allow"+rate_limit/approval_protocol)Best Practices (
<AccordionGroup>)PRAISONAI_CALLBACK_SECRETin production.owner_user_id(Telegram numeric, Discord snowflake, SlackUxxxxx)."allow"with rate limiting and tool approval.Related (
<CardGroup cols={2}>)docs/features/messaging-botsdocs/best-practices/bot-security3. UPDATE
docs/best-practices/bot-security.mdxThe current file (lines 217–265) says:
This is now incorrect — the pairing system is shipped.
Required changes:
<Warning>that pairing is "planned"./pair abc12345-style user-side example with the new owner-DM flow (Approve / Deny buttons) as the primary pattern.praisonai pairing approve …flow as the documented fallback, not the main flow.unknown_user_policy="pair"is now the entry point, withowner_user_idbeing the key new config.PRAISONAI_CALLBACK_SECRETto the "Self-Hoster Security Checklist → Gateway Pairing Active" accordion (in addition to the existingPRAISONAI_GATEWAY_SECRET).4. UPDATE
docs.jsonUnder the Features → Communication & Messaging group (currently at lines 281–294 of
docs.json), add the new page right afterbot-routing:"pages": [ "docs/features/messaging-bots", "docs/features/email-bot", "docs/features/whatsapp-bot", "docs/features/bot-commands", "docs/features/bot-gateway", "docs/features/bot-routing", + "docs/features/bot-unknown-user-pairing", "docs/features/botos", "docs/features/push-notifications" ]Validate JSON after editing.
Style reminders (from AGENTS.md)
unknown_user_policy="pair"+owner_user_idfirst; HMAC / fallback / CLI deeper in the page.from praisonaiagents.bots import BotConfig— not deep sub-module paths.#8B0000(inputs/user),#189AB4(bot/process),#10B981(success/approved),#F59E0B(pending/intermediate),#6366F1(config). White text,#7C90A0strokes.messaging-bots.mdx.docs/concepts/— those are human-approved only.Acceptance criteria
docs/features/messaging-bots.mdxlistsunknown_user_policyandowner_user_idin both the example code block and the options table, with a<Note>linking to the new page.docs/features/bot-unknown-user-pairing.mdxexists with: frontmatter, hero mermaid, Quick Start (<Steps>), How It Works sequenceDiagram, Policies table + decision mermaid, CLI fallback, HMAC-signing security note +PRAISONAI_CALLBACK_SECRETwarning, per-platform accordion, Common Patterns, Best Practices (<AccordionGroup>), Related (<CardGroup>).docs/best-practices/bot-security.mdxno longer describes pairing as "planned"; examples reflect the owner-DM button flow and addPRAISONAI_CALLBACK_SECRETto the checklist.docs.jsonregistersdocs/features/bot-unknown-user-pairingunder "Communication & Messaging" and remains valid JSON.from praisonaiagents...imports that actually resolve against PR #1518.docs/concepts/is modified.Generated by documentation-review agent based on merged PR MervinPraison/PraisonAI#1518.