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

# Webhooks

> Get real-time notifications for security events

## Overview

Webhooks allow you to receive real-time HTTP notifications when security events occur. Use them to integrate Bloom with your alerting systems (Slack, PagerDuty, etc.).

## Supported Events

| Event                 | Description                                      |
| --------------------- | ------------------------------------------------ |
| `injection_blocked`   | Prompt injection attack was detected and blocked |
| `agent_killed`        | An agent was terminated via kill switch          |
| `anomaly_detected`    | Unusual agent behavior was detected              |
| `permission_denied`   | Agent attempted unauthorized action              |
| `rate_limit_exceeded` | Agent exceeded rate limits                       |
| `mcp_tool_blocked`    | MCP tool call was blocked by permissions         |

## Creating a Webhook

### Dashboard

1. Go to **Activity** > **Webhooks** tab
2. Click **Add Webhook**
3. Configure:
   * **Name**: Descriptive name
   * **URL**: Your HTTPS endpoint
   * **Secret**: Optional HMAC signing secret
   * **Events**: Select which events to receive

### API

```bash theme={null}
curl -X POST https://api.bloomtechnologies.app/webhooks \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Security Alerts",
    "url": "https://your-server.com/webhook",
    "secret": "your-hmac-secret",
    "events": ["injection_blocked", "agent_killed", "anomaly_detected"]
  }'
```

## Webhook Payload

All webhooks include:

```json theme={null}
{
  "id": "evt_abc123",
  "event": "injection_blocked",
  "timestamp": "2026-02-01T15:30:00Z",
  "organization_id": "org_xxx",
  "agent_id": "agent_abc123",
  "agent_name": "my-agent",
  "data": {
    // Event-specific data
  }
}
```

### Event-Specific Data

<Tabs>
  <Tab title="injection_blocked">
    ```json theme={null}
    {
      "event": "injection_blocked",
      "data": {
        "pattern_name": "instruction_override",
        "severity": "critical",
        "matched_text": "ignore all previous...",
        "location": "body.messages[0].content",
        "endpoint": "/v1/chat/completions",
        "source_ip": "192.168.1.1"
      }
    }
    ```
  </Tab>

  <Tab title="agent_killed">
    ```json theme={null}
    {
      "event": "agent_killed",
      "data": {
        "reason": "Manual kill by admin",
        "triggered_by": "user@example.com",
        "kill_method": "dashboard",
        "requests_terminated": 2,
        "propagation_ms": 47
      }
    }
    ```
  </Tab>

  <Tab title="anomaly_detected">
    ```json theme={null}
    {
      "event": "anomaly_detected",
      "data": {
        "anomaly_score": 0.87,
        "anomaly_types": ["frequency", "new_endpoint"],
        "baseline_deviation": {
          "normal_rpm": 10,
          "current_rpm": 150
        },
        "action_taken": "alert"
      }
    }
    ```
  </Tab>

  <Tab title="permission_denied">
    ```json theme={null}
    {
      "event": "permission_denied",
      "data": {
        "attempted_action": "POST /v1/fine-tuning/jobs",
        "service": "openai",
        "reason": "No scope grants access to this endpoint",
        "agent_scopes": ["openai-chat-only"]
      }
    }
    ```
  </Tab>
</Tabs>

## Signature Verification

If you provide a secret, Bloom signs each webhook with HMAC-SHA256:

```
X-Bloom-Signature: sha256=abc123...
X-Bloom-Timestamp: 1706803800
```

### Verification Example (Python)

```python theme={null}
import hmac
import hashlib

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

# In your webhook handler
@app.post("/webhook")
async def handle_webhook(request: Request):
    payload = await request.body()
    signature = request.headers.get("X-Bloom-Signature")

    if not verify_webhook(payload, signature, WEBHOOK_SECRET):
        raise HTTPException(401, "Invalid signature")

    event = json.loads(payload)
    # Process event...
```

### Verification Example (Node.js)

```javascript theme={null}
const crypto = require('crypto');

function verifyWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(`sha256=${expected}`),
    Buffer.from(signature)
  );
}
```

## Retry Policy

Failed webhook deliveries are retried:

| Attempt | Delay      |
| ------- | ---------- |
| 1       | Immediate  |
| 2       | 1 minute   |
| 3       | 5 minutes  |
| 4       | 30 minutes |
| 5       | 2 hours    |

After 5 failures, the webhook is marked as failing and you'll receive an email notification.

## Testing Webhooks

### From Dashboard

1. Go to **Activity** > **Webhooks**
2. Find your webhook
3. Click the **Test** button (paper plane icon)

This sends a test payload to verify your endpoint is working.

### Test Payload

```json theme={null}
{
  "id": "evt_test_123",
  "event": "test",
  "timestamp": "2026-02-01T15:30:00Z",
  "data": {
    "message": "This is a test webhook from Bloom"
  }
}
```

## Integration Examples

### Slack

```python theme={null}
import requests

@app.post("/webhook")
async def handle_bloom_webhook(request: Request):
    event = await request.json()

    slack_message = {
        "text": f"🚨 Bloom Alert: {event['event']}",
        "blocks": [
            {
                "type": "section",
                "text": {
                    "type": "mrkdwn",
                    "text": f"*Event:* {event['event']}\n*Agent:* {event['agent_name']}\n*Time:* {event['timestamp']}"
                }
            }
        ]
    }

    requests.post(SLACK_WEBHOOK_URL, json=slack_message)
```

### PagerDuty

```python theme={null}
@app.post("/webhook")
async def handle_bloom_webhook(request: Request):
    event = await request.json()

    if event["event"] in ["agent_killed", "injection_blocked"]:
        requests.post(
            "https://events.pagerduty.com/v2/enqueue",
            json={
                "routing_key": PAGERDUTY_KEY,
                "event_action": "trigger",
                "payload": {
                    "summary": f"Bloom: {event['event']} - {event['agent_name']}",
                    "severity": "critical",
                    "source": "bloom-iam"
                }
            }
        )
```

## Managing Webhooks

### List Webhooks

```bash theme={null}
curl https://api.bloomtechnologies.app/webhooks \
  -H "Authorization: Bearer $API_KEY"
```

### Delete Webhook

```bash theme={null}
curl -X DELETE https://api.bloomtechnologies.app/webhooks/{webhook_id} \
  -H "Authorization: Bearer $API_KEY"
```

### View Webhook Stats

In the dashboard, each webhook shows:

* Success/failure counts
* Last triggered time
* Recent delivery attempts
