> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Verification

> Fail-closed HMAC signature verification for inbound bot webhooks

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(name="webhook-agent", instructions="Verify and process incoming webhooks.")
agent.start("Verify the HMAC signature on this incoming webhook from GitHub.")
```

Inbound webhooks are verified by default — misconfigured secrets return HTTP 401 instead of silently accepting unsigned traffic.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
export LINEAR_WEBHOOK_SECRET=your-linear-signing-secret
export LINEAR_OAUTH_TOKEN=your-linear-token
praisonai bot linear --token "$LINEAR_OAUTH_TOKEN"
```

The user posts a webhook; HMAC verification runs before the agent handles the payload.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    WH[Inbound Webhook] --> Gate[enforce_webhook_verification]
    Gate -->|verified| Agent[Agent run]
    Gate -->|rejected| Deny[HTTP 401]

    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef gate fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
    classDef deny fill:#8B0000,stroke:#7C90A0,color:#fff

    class WH input
    class Gate gate
    class Agent ok
    class Deny deny
```

## Quick Start

<Steps>
  <Step title="Set your signing secret">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    export LINEAR_WEBHOOK_SECRET=your-linear-secret
    # or for AgentMail:
    export AGENTMAIL_WEBHOOK_SECRET=your-agentmail-secret
    ```
  </Step>

  <Step title="Run the bot — verification is on by default">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai bot linear --token $LINEAR_OAUTH_TOKEN
    ```
  </Step>

  <Step title="Local dev without a real platform">
    <Warning>
      Never set this in production.
    </Warning>

    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    PRAISONAI_INSECURE_WEBHOOKS=true praisonai bot linear --token $LINEAR_OAUTH_TOKEN
    ```
  </Step>
</Steps>

## Which mode am I in?

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
flowchart TB
    Start{Secret set?} -->|Yes| Prod[Fail-closed verification — production]
    Start -->|No| Dev{PRAISONAI_INSECURE_WEBHOOKS?}
    Dev -->|true| Skip[Skip verification — dev only, logs warning]
    Dev -->|unset/false| Reject[Reject all webhooks — HTTP 401]

    classDef cfg fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef warn fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
    classDef deny fill:#8B0000,stroke:#7C90A0,color:#fff

    class Start,Dev cfg
    class Skip warn
    class Prod ok
    class Reject deny
```

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant Platform
    participant Bot
    participant Gate as enforce_webhook_verification
    participant Verifier as HmacWebhookVerifier
    participant Agent

    Platform->>Bot: POST webhook
    Bot->>Gate: headers + raw body
    alt verified
        Gate->>Verifier: verify()
        Verifier-->>Gate: true
        Gate->>Agent: dispatch
    else rejected
        Gate-->>Bot: false
        Bot-->>Platform: HTTP 401
    end
```

| accepts\_webhooks | Verifier configured | PRAISONAI\_INSECURE\_WEBHOOKS | Outcome                |
| ----------------- | ------------------- | ----------------------------- | ---------------------- |
| false             | —                   | —                             | No verification gate   |
| true              | yes                 | unset                         | Verify; 401 on failure |
| true              | no                  | unset                         | **401** (fail-closed)  |
| true              | —                   | true                          | Skip with warning      |

## Configuration Options

| Variable                      | Purpose                                                                                      |
| ----------------------------- | -------------------------------------------------------------------------------------------- |
| `PRAISONAI_INSECURE_WEBHOOKS` | Set to `true` / `1` / `yes` to disable verification globally (local dev only)                |
| `LINEAR_WEBHOOK_SECRET`       | HMAC secret for Linear webhooks                                                              |
| `AGENTMAIL_WEBHOOK_SECRET`    | HMAC secret for AgentMail webhooks (required in production; API token is **not** a fallback) |
| `WHATSAPP_APP_SECRET`         | Meta app secret for Cloud-mode WhatsApp webhooks                                             |

| PlatformCapabilities field   | Default | Meaning                                   |
| ---------------------------- | ------- | ----------------------------------------- |
| `accepts_webhooks`           | `False` | Channel receives inbound HTTP webhooks    |
| `verifies_webhook_signature` | `False` | Adapter exposes a verifier implementation |

## Common Patterns

**Built-in adapter, secret only** — set the platform env var and run the bot; no code required.

**Custom adapter with `HmacWebhookVerifier`:**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.bots.webhook_security import HmacWebhookVerifier

verifier = HmacWebhookVerifier(
    secret="your-secret",
    signature_headers=["X-Hub-Signature-256"],
    prefix="sha256=",
)
```

**Custom ingress with the shared gate:**

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai.bots.webhook_security import enforce_webhook_verification

ok = enforce_webhook_verification(
    accepts_webhooks=True,
    verifier=verifier,
    headers=dict(request.headers),
    raw_body=await request.body(),
    platform="mychat",
)
if not ok:
    return Response(status_code=401)
```

## Best Practices

<AccordionGroup>
  <Accordion title="Never set PRAISONAI_INSECURE_WEBHOOKS in production">
    The override logs a warning on every request and disables all signature checks.
  </Accordion>

  <Accordion title="Use the shared helper instead of rolling your own HMAC">
    `verify_hmac` and `HmacWebhookVerifier` fail closed on missing secrets, unknown digest algorithms, and verifier exceptions.
  </Accordion>

  <Accordion title="Rotate signing secrets with the platform">
    Update both the platform webhook config and your env var at the same time.
  </Accordion>
</AccordionGroup>

<Note>
  This page covers **outbound bot webhook signature verification** (verifying that requests to your bot are from the real platform). For **inbound event triggers** — pointing external services at your gateway to run an agent — see [Gateway Inbound Hooks](/docs/features/gateway-hooks).
</Note>

## Related

<CardGroup cols={2}>
  <Card title="Linear Bot" icon="list" href="/docs/features/linear-bot">
    Linear webhook setup and secrets
  </Card>

  <Card title="Email Bot" icon="envelope" href="/docs/features/email-bot">
    AgentMail webhook mode
  </Card>

  <Card title="Gateway Inbound Hooks" icon="webhook" href="/docs/features/gateway-hooks">
    Trigger agents from external services via POST /hooks/\<path>
  </Card>

  <Card title="Platform Capabilities" icon="sliders" href="/docs/features/bot-platform-capabilities">
    accepts\_webhooks and verifies\_webhook\_signature
  </Card>
</CardGroup>
