# Webhook Events Integration

Instrument any AI agent with Capsule Security's detection pipeline using a generic HTTP webhook interface for real-time prevention and observability.

## Overview

The Webhook Events integration is a platform-agnostic HTTP API that lets you connect **any** AI agent - custom-built agents, orchestration frameworks, internal tools - to Capsule Security. Unlike IDE-specific integrations (Claude Code, Cursor, GitHub Copilot), this integration requires you to add HTTP calls directly in your agent code.

Two modes of operation are supported:

- **Prevention** (synchronous) - your agent sends an event *before* executing an action and waits for an allow/block decision
- **Telemetry** (asynchronous) - your agent fires an event *after* an action completes for observability, without blocking execution


```
Your Agent → HTTP POST → Capsule → Detection Pipeline → Allow/Block Response
```

Use this integration when:

- You are building a custom AI agent or orchestration pipeline
- Your platform does not have a native Capsule integration
- You need programmatic control over when and how events are sent


## Event Types

### Blocking Events

Synchronous - your agent waits for an allow/block response before proceeding.

| Event | Description |
|  --- | --- |
| `tool_invocation` | Before a tool or action executes |
| `user_message` | Before a user prompt is processed |


### Observational Events

Fire-and-forget - Capsule responds with `202 Accepted`.

| Event | Description |
|  --- | --- |
| `tool_result` | After tool execution completes |
| `agent_message` | Agent response text |
| `session_start` | Session begins |
| `session_end` | Session terminates |


## Prerequisites

Before you begin, ensure you have:

- A **Capsule Security** account with admin access
- An AI agent or application you want to instrument


## Step 1: Generate API Credentials

1. Log in to the **Capsule Security** portal
2. Navigate to **Integrations** and locate **Webhook Events**
3. Click **Install** - Capsule generates a JWT token scoped to your tenant
4. Copy the token and store it securely (it will not be shown again)


## Step 2: Instrument Your Agent

Add HTTP calls to your agent code at the appropriate lifecycle points. Refer to the API Reference and Integration Examples sections below.

## API Reference

### Endpoint

```
POST https://{capsule-host}/v1/generic-webhook/hooks/events
```

### Headers

| Header | Required | Description |
|  --- | --- | --- |
| `Authorization` | Yes | `Bearer <jwt-token>` |
| `x-correlation-id` | No | Trace ID for request correlation |


### Request Body

All events share a common envelope with event-specific fields.

The `agent` object and its fields are optional. When `agent.id` is omitted, Capsule attributes the event to the user's personal agent, derived from `user.email` - use this for assistants that act on behalf of a single user rather than as a named, shared agent. When `agent.name` is omitted, the agent is named after its id, or "{email}'s agent" for personal agents.

#### `tool_invocation`

Sent **before** a tool executes. Blocks until Capsule responds.

```json
{
  "event_type": "tool_invocation",
  "session_id": "sess-abc123",
  "timestamp": "2026-03-03T10:30:00Z",
  "agent": {
    "id": "support-agent",
    "name": "Customer Support Agent"
  },
  "user": {
    "email": "user@example.com"
  },
  "tool": {
    "name": "database_query",
    "input": { "query": "SELECT * FROM users" }
  }
}
```

#### `user_message`

Sent **before** a user prompt is processed. Blocks until Capsule responds.

```json
{
  "event_type": "user_message",
  "session_id": "sess-abc123",
  "timestamp": "2026-03-03T10:30:00Z",
  "agent": {
    "id": "support-agent",
    "name": "Customer Support Agent"
  },
  "user": {
    "email": "user@example.com"
  },
  "message": {
    "content": "Show me all customer records from the last month"
  }
}
```

#### `tool_result`

Sent **after** tool execution completes. Fire-and-forget.

```json
{
  "event_type": "tool_result",
  "session_id": "sess-abc123",
  "timestamp": "2026-03-03T10:30:05Z",
  "agent": {
    "id": "support-agent",
    "name": "Customer Support Agent"
  },
  "user": {
    "email": "user@example.com"
  },
  "tool": {
    "name": "database_query",
    "input": { "query": "SELECT * FROM users" },
    "output": { "row_count": 42 }
  }
}
```

#### `agent_message`

Sent after the agent produces a response. Fire-and-forget.

```json
{
  "event_type": "agent_message",
  "session_id": "sess-abc123",
  "timestamp": "2026-03-03T10:30:06Z",
  "agent": {
    "id": "support-agent",
    "name": "Customer Support Agent"
  },
  "user": {
    "email": "user@example.com"
  },
  "message": {
    "content": "Here are the 42 customer records from the last month..."
  }
}
```

#### `session_start`

Sent when a session begins. Fire-and-forget.

```json
{
  "event_type": "session_start",
  "session_id": "sess-abc123",
  "timestamp": "2026-03-03T10:29:55Z",
  "agent": {
    "id": "support-agent",
    "name": "Customer Support Agent"
  },
  "user": {
    "email": "user@example.com"
  }
}
```

#### `session_end`

Sent when a session terminates. Fire-and-forget.

```json
{
  "event_type": "session_end",
  "session_id": "sess-abc123",
  "timestamp": "2026-03-03T10:45:00Z",
  "agent": {
    "id": "support-agent",
    "name": "Customer Support Agent"
  },
  "user": {
    "email": "user@example.com"
  }
}
```

### Responses

#### Blocking Events (`tool_invocation`, `user_message`)

**Allowed:**

```json
{
  "apiVersion": "1",
  "response": {
    "action": "allow"
  }
}
```

**Blocked:**

```json
{
  "apiVersion": "1",
  "response": {
    "action": "block",
    "reason": "Policy violation: query accesses sensitive table"
  }
}
```

#### Observational Events

`202 Accepted` with an empty body.

### Error Codes

| Code | Meaning | Recommended Action |
|  --- | --- | --- |
| `400` | Malformed request body | Fix the payload and retry |
| `401` | Invalid or expired JWT | Regenerate credentials in the Capsule portal |
| `408` | Request timeout | Treat as allow (fail-open) |
| `500` | Server error | Treat as allow (fail-open) |


## Integration Examples

### Pre-Execution Guard

Call `tool_invocation` before executing a tool. If the response contains `"action": "block"`, deny the action.

```bash
curl -X POST "$CAPSULE_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "tool_invocation",
    "session_id": "sess-abc123",
    "timestamp": "2026-03-03T10:30:00Z",
    "agent": {"id": "my-agent", "name": "My Agent"},
    "user": {"email": "user@example.com"},
    "tool": {"name": "database_query", "input": {"query": "SELECT * FROM users"}}
  }'
```

**Allowed response:**

```json
{
  "apiVersion": "1",
  "response": {
    "action": "allow"
  }
}
```

**Blocked response:**

```json
{
  "apiVersion": "1",
  "response": {
    "action": "block",
    "reason": "Policy violation: query accesses sensitive table"
  }
}
```

### Telemetry Reporter

Fire-and-forget `tool_result` after execution. Capsule responds with `202 Accepted`.

```bash
curl -X POST "$CAPSULE_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "tool_result",
    "session_id": "sess-abc123",
    "timestamp": "2026-03-03T10:30:05Z",
    "agent": {"id": "my-agent", "name": "My Agent"},
    "user": {"email": "user@example.com"},
    "tool": {"name": "database_query", "input": {"query": "SELECT * FROM users"}, "output": {"row_count": 42}}
  }'
```

### Full Session Lifecycle

A complete sequence showing all event types.

```bash
CAPSULE_URL="https://{capsule-host}/v1/generic-webhook/hooks/events"
TOKEN="your-jwt-token"
SESSION_ID="sess-$(uuidgen)"

# 1. Session start
curl -X POST "$CAPSULE_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "session_start",
    "session_id": "'"$SESSION_ID"'",
    "timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",
    "agent": {"id": "my-agent", "name": "My Agent"},
    "user": {"email": "user@example.com"}
  }'

# 2. User message (blocking)
curl -X POST "$CAPSULE_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "user_message",
    "session_id": "'"$SESSION_ID"'",
    "timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",
    "agent": {"id": "my-agent", "name": "My Agent"},
    "user": {"email": "user@example.com"},
    "message": {"content": "Look up order #12345"}
  }'

# 3. Tool invocation (blocking)
curl -X POST "$CAPSULE_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "tool_invocation",
    "session_id": "'"$SESSION_ID"'",
    "timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",
    "agent": {"id": "my-agent", "name": "My Agent"},
    "user": {"email": "user@example.com"},
    "tool": {"name": "order_lookup", "input": {"order_id": "12345"}}
  }'

# 4. Tool result (fire-and-forget)
curl -X POST "$CAPSULE_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "tool_result",
    "session_id": "'"$SESSION_ID"'",
    "timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",
    "agent": {"id": "my-agent", "name": "My Agent"},
    "user": {"email": "user@example.com"},
    "tool": {"name": "order_lookup", "input": {"order_id": "12345"}, "output": {"status": "shipped", "tracking": "1Z999AA10123456784"}}
  }'

# 5. Agent message (fire-and-forget)
curl -X POST "$CAPSULE_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "agent_message",
    "session_id": "'"$SESSION_ID"'",
    "timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",
    "agent": {"id": "my-agent", "name": "My Agent"},
    "user": {"email": "user@example.com"},
    "message": {"content": "Order #12345 has been shipped. Tracking number: 1Z999AA10123456784"}
  }'

# 6. Session end (fire-and-forget)
curl -X POST "$CAPSULE_URL" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "session_end",
    "session_id": "'"$SESSION_ID"'",
    "timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",
    "agent": {"id": "my-agent", "name": "My Agent"},
    "user": {"email": "user@example.com"}
  }'
```

## Timeout and Reliability

| Setting | Blocking Events | Telemetry Events |
|  --- | --- | --- |
| Server timeout | 10s | 10s |
| Recommended client timeout | 10s | 5s |


**Always fail-open**: if Capsule is unreachable or returns a server error, allow the action to proceed. Telemetry failures must never block agent operation.

## Verify the Integration

1. Send a `session_start` event followed by a `tool_invocation` event using the examples above
2. Log in to the **Capsule Security** portal
3. Navigate to **Inventory > Agents** and confirm your agent appears
4. Click on the agent and review the audit logs to verify events are captured
5. To view the full conversation, navigate to **Observability** and filter by **Activity Type - Session**


## Troubleshooting

| Symptom | Cause | Resolution |
|  --- | --- | --- |
| `401 Unauthorized` | Invalid or expired JWT | Regenerate credentials in **Integrations → Webhook Events** |
| Events not appearing in portal | Incorrect endpoint URL or malformed payload | Verify the URL and validate your JSON against the examples above |
| Blocking events always returning `allow` | No active policies configured | Configure detection policies in the Capsule portal |
| High latency on blocking events | Network distance to Capsule instance | Ensure your agent is in the same region as your Capsule deployment |
| `408 Request Timeout` | Capsule processing exceeded timeout | Your agent should treat this as `allow` (fail-open) |


## Security Considerations

- Store JWT tokens in a secrets manager or environment variables - never hardcode them in source code
- Use HTTPS exclusively - all Capsule endpoints require TLS
- Rotate tokens periodically via the Capsule portal
- Always fail-open - never let a dropped link to Capsule block your agent's operation


## Support

For help with this integration, contact support.

## References

- [JWT.io](https://jwt.io) - debug and inspect JWT tokens