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 |
Capsule decides each blocking event from that event alone, without the session's earlier events, so send everything the decision needs, including agent.instructions, on every blocking event. Capsule checks the session again later with its full history. That check can raise new detections but does not change a decision already returned.
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.
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. Send the agent's system prompt in agent.instructions to record it on the agent in Capsule inventory; the latest non-empty value wins. Include it on every user_message and tool_invocation event, not only on session_start: the allow/block decision only sees instructions sent in the same event.
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",
"instructions": "You are a customer support agent. Answer billing questions and escalate refund requests to a human."
},
"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",
"instructions": "You are a customer support agent. Answer billing questions and escalate refund requests to a human."
},
"user": {
"email": "user@example.com"
},
"message": {
"content": "Show me all customer records from the last month"
}
}Sent after tool execution completes. Fire-and-forget. tool.output can be a string or a JSON object.
{
"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",
"instructions": "You are a customer support agent. Answer billing questions and escalate refund requests to a human."
},
"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) |
415 | Body is not JSON | Send the body with Content-Type: application/json |
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", "instructions": "You are an order lookup assistant."},
"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", "instructions": "You are an order lookup assistant."},
"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", "instructions": "You are an order lookup assistant."},
"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", "instructions": "You are an order lookup assistant."},
"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 | 30s | 30s |
| Recommended client timeout | 35s | 5s |
Most blocking decisions return within a few seconds, but an event that needs deeper analysis can take up to 30s. A client that gives up sooner and fails open lets through actions Capsule would have blocked. The client timeout sits above the server timeout because your timer starts first and also covers network transit.
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