Instrument any AI agent with Capsule Security's detection pipeline using a generic HTTP webhook interface for real-time prevention and observability.
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 ResponseUse 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
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 |
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 |
Before you begin, ensure you have:
- A Capsule Security account with admin access
- An AI agent or application you want to instrument
Log in to the Capsule Security portal
Navigate to Integrations and locate Webhook Events
Click Install — Capsule generates a JWT token scoped to your tenant
Copy the token and store it securely (it will not be shown again)
Add HTTP calls to your agent code at the appropriate lifecycle points. Refer to the API Reference and Integration Examples sections below.
POST https://{capsule-host}/v1/generic-webhook/hooks/events| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <jwt-token> |
x-correlation-id | No | Trace ID for request correlation |
All events share a common envelope with event-specific fields.
Sent before a tool executes. Blocks until Capsule responds.
{
"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" }
}
}Sent before a user prompt is processed. Blocks until Capsule responds.
{
"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"
}
}Sent after tool execution completes. Fire-and-forget.
{
"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 }
}
}Sent after the agent produces a response. Fire-and-forget.
{
"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..."
}
}Sent when a session begins. Fire-and-forget.
{
"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"
}
}Sent when a session terminates. Fire-and-forget.
{
"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"
}
}Allowed:
{
"apiVersion": "1",
"response": {
"action": "allow"
}
}Blocked:
{
"apiVersion": "1",
"response": {
"action": "block",
"reason": "Policy violation: query accesses sensitive table"
}
}202 Accepted with an empty body.
| 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) |
Call tool_invocation before executing a tool. If the response contains "action": "block", deny the action.
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:
{
"apiVersion": "1",
"response": {
"action": "allow"
}
}Blocked response:
{
"apiVersion": "1",
"response": {
"action": "block",
"reason": "Policy violation: query accesses sensitive table"
}
}Fire-and-forget tool_result after execution. Capsule responds with 202 Accepted.
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}}
}'A complete sequence showing all event types.
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"}
}'| 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.
Send a
session_startevent followed by atool_invocationevent using the examples aboveLog in to the Capsule Security portal
Navigate to Inventory > Agents and confirm your agent appears
Click on the agent and review the audit logs to verify events are captured
To view the full conversation, navigate to Observability and filter by Activity Type — Session
| 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) |
- 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
For help with this integration, contact support.
- JWT.io — debug and inspect JWT tokens