> ## 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 Unknown-User Pairing

> Owner-DM inline-button approval for unknown users on Telegram, Discord, and Slack bots

Bot pairing enables owner-approval for unknown users with inline Approve/Deny buttons sent directly to your DM.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Unknown User Pairing Flow"
        A[👤 Unknown User] --> B[🤖 Bot]
        B --> C[🔒 Generate Code]
        C --> D[📩 Owner DM]
        D --> E{✅❌ Decision}
        E -->|Approve| F[🟢 User Paired]
        E -->|Deny| G[🔴 Request Denied]
        F --> H[💬 Future Messages]
    end
    
    classDef user fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef bot fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef approve fill:#10B981,stroke:#7C90A0,color:#fff
    classDef deny fill:#EF4444,stroke:#7C90A0,color:#fff
    
    class A user
    class B bot
    class C,D process
    class E process
    class F,H approve
    class G deny
```

## Quick Start

<Steps>
  <Step title="Enable Pairing">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonaiagents.bots import BotConfig

    config = 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.",
    )
    ```
  </Step>

  <Step title="Production Environment">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    export PRAISONAI_CALLBACK_SECRET="$(openssl rand -hex 32)"
    ```

    Without this, inline-button callbacks stop working across bot restarts.
  </Step>
</Steps>

***

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
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
```

The pairing system intercepts messages from users not in `allowed_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).

***

## Policy Options

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    MSG["📩 Message from Unknown User"] --> POLICY{"unknown_user_policy?"}
    
    POLICY -->|"deny"| DENY["🔇 Silently drop message"]
    POLICY -->|"pair"| PAIR["🔗 Generate pairing code"]
    POLICY -->|"allow"| ALLOW["✅ Process message directly"]
    
    PAIR --> OWNER_ID{"owner_user_id set?"}
    OWNER_ID -->|"Yes"| INLINE["📱 Inline approval buttons"]
    OWNER_ID -->|"No"| CLI["📝 CLI fallback instructions"]
    
    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef check fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef deny fill:#EF4444,stroke:#7C90A0,color:#fff
    classDef allow fill:#10B981,stroke:#7C90A0,color:#fff
    classDef pair fill:#189AB4,stroke:#7C90A0,color:#fff
    
    class MSG input
    class POLICY,OWNER_ID check
    class DENY deny
    class ALLOW allow
    class PAIR,INLINE,CLI pair
```

| 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?

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TD
    START["🤖 Setting up a bot?"] --> USERS{"Who should use it?"}
    
    USERS -->|"Only me/team"| INTERNAL["Internal Bot"]
    USERS -->|"Selected people"| GATED["Gated Bot"]  
    USERS -->|"Anyone"| PUBLIC["Public Bot"]
    
    INTERNAL --> DENY_REC["✅ Use 'deny'<br/>+ allowed_users list"]
    GATED --> PAIR_REC["✅ Use 'pair'<br/>+ owner_user_id"]
    PUBLIC --> ALLOW_REC["✅ Use 'allow'<br/>+ rate limiting"]
    
    classDef start fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef question fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef option fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef recommendation fill:#10B981,stroke:#7C90A0,color:#fff
    
    class START start
    class USERS question
    class INTERNAL,GATED,PUBLIC option
    class DENY_REC,PAIR_REC,ALLOW_REC recommendation
```

***

## 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
```

The owner can then approve manually:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
praisonai pairing approve <channel_type> <code>
```

Where `<channel_type>` is one of: `telegram`, `discord`, `slack`.

***

## Security: HMAC-signed Callbacks

All inline-button callbacks are cryptographically signed to prevent tampering:

* **Callback format**: `pair:{action}:{channel}:{user_id}:{code}:{sig}`
* **Signature**: First 8 hex chars of `HMAC-SHA256(PRAISONAI_CALLBACK_SECRET, payload)`
* **Verification**: Tampered `callback_data` fails verification and is silently ignored + logged

<Warning>
  Without `PRAISONAI_CALLBACK_SECRET` set in your environment, a random per-process secret is used and inline buttons stop working after bot restart. Always set this in production:

  ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
  export PRAISONAI_CALLBACK_SECRET="$(openssl rand -hex 32)"
  ```
</Warning>

***

## Platform-specific UI

<AccordionGroup>
  <Accordion title="Telegram">
    Uses `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:**

    ```
    User @username wants to chat. Approve access?
    [✅ Approve] [❌ Deny]
    ```

    **Implementation:** Telegram's `InlineKeyboardButton` with `callback_data` containing the signed pairing payload.
  </Accordion>

  <Accordion title="Discord">
    Uses `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:**

    ```
    User username#1234 wants to chat. Approve access?
    [✅ Approve] [❌ Deny]
    ```

    **Implementation:** Discord's Button components in an Action Row with HMAC-signed `custom_id` values.
  </Accordion>

  <Accordion title="Slack">
    Uses Block Kit `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:**

    ```
    *@username* wants to chat. Approve access?
    [✅ Approve] [❌ Deny]
    ```

    **Implementation:** Slack Block Kit buttons with HMAC-signed values and dedicated action handlers.
  </Accordion>
</AccordionGroup>

***

## Configuration Options

For the complete `BotConfig` options including `unknown_user_policy` and `owner_user_id`, see the canonical reference at [Messaging Bots Configuration](/docs/features/messaging-bots#configuration-options).

***

## Common Patterns

<Tabs>
  <Tab title="Semi-public Bot">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonaiagents.bots import BotConfig

    config = BotConfig(
        token="your-bot-token",
        unknown_user_policy="pair",    # Enable approval workflow
        owner_user_id="123456789",     # Your platform user ID
    )

    agent = Agent(
        name="Support Bot",
        instructions="Help users with their questions",
    )
    ```

    Perfect for customer support or community bots where you want to vet new users.
  </Tab>

  <Tab title="Internal Bot">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent  
    from praisonaiagents.bots import BotConfig

    config = BotConfig(
        token="your-bot-token",
        unknown_user_policy="deny",           # Block unknown users
        allowed_users=["user123", "user456"], # Explicit allowlist
    )

    agent = Agent(
        name="Internal Assistant", 
        instructions="Help with internal tasks",
    )
    ```

    For team or company-internal bots with a fixed user list.
  </Tab>

  <Tab title="Public Bot">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonaiagents.bots import BotConfig

    config = BotConfig(
        token="your-bot-token", 
        unknown_user_policy="allow",     # Open to everyone
        auto_approve_tools=False,        # Still require tool approval
    )

    agent = Agent(
        name="Public Assistant",
        instructions="Help anyone with general questions",
    )
    ```

    For fully public bots. Combine with rate limiting and tool approval for safety.
  </Tab>
</Tabs>

***

## Best Practices

<AccordionGroup>
  <Accordion title="Always set PRAISONAI_CALLBACK_SECRET in production">
    Generate a strong secret and set it as an environment variable:

    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    # Generate and export
    export PRAISONAI_CALLBACK_SECRET="$(openssl rand -hex 32)"

    # Or set permanently in your deployment
    echo "PRAISONAI_CALLBACK_SECRET=$(openssl rand -hex 32)" >> .env
    ```

    Without this, inline buttons stop working when your bot restarts.
  </Accordion>

  <Accordion title="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`)

    Find your ID by messaging the bot directly and checking the logs, or use platform-specific methods.
  </Accordion>

  <Accordion title="Combine 'allow' policy with rate limiting and tool approval">
    If using `unknown_user_policy="allow"` for a public bot, protect yourself with:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    config = BotConfig(
        unknown_user_policy="allow",
        auto_approve_tools=False,     # Users still need approval for dangerous tools
        debounce_ms=2000,            # Coalesce rapid messages
    )
    ```

    Consider also implementing rate limiting at the platform level.
  </Accordion>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="Messaging Bots" icon="robot" href="/docs/features/messaging-bots">
    Complete bot configuration and setup
  </Card>

  <Card title="Bot Security" icon="shield" href="/docs/best-practices/bot-security">
    Security best practices for bots
  </Card>

  <Card title="Web-UI HTTP API Approval" icon="globe" href="/docs/features/bot-pairing#web-ui-http-api-approval">
    HTTP API endpoints for web admin UI approval
  </Card>
</CardGroup>
