Webhooks let you receive real-time notifications at your own URL when subscribed events occur. Use them to forward events to your SIEM, build approval workflows, notify on-call, or trigger automations.Documentation Index
Fetch the complete documentation index at: https://docs.getunbound.ai/llms.txt
Use this file to discover all available pages before exploring further.
Prerequisites
- Unbound account with Admin role
- A public HTTPS endpoint you control to receive the events
- (Optional) A server-side secret manager to store the signing secret
Setup
- In your Unbound dashboard, go to Settings → Webhooks
- Click Add Endpoint
- Enter your endpoint URL (must be
https://, public, and not pointing at a private network) - Optionally add a description
- Select one or more events to subscribe to (or Select all)
- Click Create
whsec_… signing secret is shown once. Copy it now and store it in your secret manager. You can reveal it again from the endpoint detail page if you need to, but treat it like an API key.
Signature verification
Every delivery includes three headers your receiver can use to verify the event came from Unbound and wasn’t tampered with in transit.| Header | Description |
|---|---|
webhook-id | Unique idempotency key (msg_…). Use it to deduplicate retries. |
webhook-timestamp | Unix seconds when we signed the payload. Reject deliveries more than ~5 minutes old to prevent replay. |
webhook-signature | v1,<base64-hmac-sha256> computed over ${webhook-id}.${webhook-timestamp}.${raw-body} using your signing secret as the HMAC key. |
- Strip the
whsec_prefix from your signing secret and base64-decode the remainder. The result is the HMAC key. - Build the signed payload by concatenating
<webhook-id>.<webhook-timestamp>.<raw-body>. - Compute
HMAC-SHA256(key, signed_payload)and base64-encode the digest. Prefix it withv1,. - Compare against the
webhook-signatureheader using a constant-time comparison. Multiple signatures may be space-separated; accept if any one matches. - Reject deliveries whose
webhook-timestampis older than 5 minutes to prevent replay.
Always verify against the raw request body bytes, not a re-serialised JSON object. JSON re-serialisation can reorder keys or change whitespace, breaking the signature.
Event types
Webhooks fire only when high-risk activity is detected. The event name is derived from the matched policy action; if no policy matched but the activity was still flagged as high-risk, you receive a.flagged event.
| Event | Fires when |
|---|---|
terminal_command.blocked | A high-risk terminal command was blocked by a policy |
terminal_command.warned | A high-risk terminal command matched a Warn policy |
terminal_command.slack_approval_requested | A high-risk terminal command is awaiting Slack approval |
terminal_command.audited | A high-risk terminal command matched an Audit policy |
terminal_command.flagged | A high-risk terminal command was detected but no policy matched |
mcp_tool.blocked | A high-risk MCP tool call was blocked by a policy |
mcp_tool.warned | A high-risk MCP tool call matched a Warn policy |
mcp_tool.slack_approval_requested | A high-risk MCP tool call is awaiting Slack approval |
mcp_tool.audited | A high-risk MCP tool call matched an Audit policy |
mcp_tool.flagged | A high-risk MCP tool call was detected but no policy matched |
terminal_command.* or mcp_tool.*), or all events (*).
Payload
Every event uses the same envelope: a top-levelid (ULID, prefixed msg_), type (the event name), timestamp (ISO-8601, UTC), and a nested data block. The data block is consistent across all event types within a family — terminal-command events carry a command string, MCP-tool events carry mcp_server, mcp_tool, and mcp_parameters instead, and everything else is shared.
Field reference
| Field | Type | Description |
|---|---|---|
tool | string | null | The AI tool that issued the call (e.g. claude-code, cursor, codex, copilot). |
tool_name | string | The specific tool name. For terminal commands this is Bash, Edit, etc.; for MCP calls it’s <server>__<tool>. |
user_email | string | null | The email of the user who owns the application that fired the call. |
prompt | string | null | The user’s original prompt that led to this activity. Useful for context. |
thread_id | string | null | Conversation thread ID for grouping related events. |
intent_attribution | string | USER_INTENTIONAL, AGENT_INITIATED, or UNKNOWN — whether the user explicitly asked for this or the agent decided autonomously. |
command | string | (terminal only) The literal command string. |
mcp_server | string | (MCP only) The MCP server the tool call targeted. |
mcp_tool | string | (MCP only) The specific tool within that server. |
mcp_parameters | object | (MCP only) The parsed arguments passed to the tool. |
matched_policies | array | Every policy that matched this event. Empty array on *.flagged events. Each entry contains id, name, action, policy_type. |
risk_score | number | Risk score on a scale of 1–10. |
risk_reason | string | Short explanation of why this was flagged as risky. |
risk_family | string | Highest-risk command family (e.g. destructive_file_op, mcp_tool). |
classifications | array | All command families this event was classified into (compound commands can produce multiple). Each entry has command_family, risk_score, risk_reason, confidence_score, targets. |
Retries
Failed deliveries (anything that isn’t HTTP 200–299) retry on the following schedule:| Attempt | Delay since previous |
|---|---|
| 1 | (immediate) |
| 2 | 5 seconds |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
| 6 | 5 hours |
| 7 | 10 hours |
| 8 | 10 hours |
Redirects (
3xx) are treated as failures and not followed. Configure your endpoint to be the resolved URL.Custom headers
You can attach static custom headers to every delivery for your endpoint — useful when your receiver requires a specific authentication header.| Receiver | Header |
|---|---|
| Splunk HEC | Authorization: Splunk <hec-token> |
| Datadog Logs Intake | DD-API-KEY: <key> |
| Sumo Logic HTTP Source | (no header — URL is the credential) |
| Tines | (no header — URL secret is the credential) |
| Slack incoming webhook | (no header — URL is the credential) |
content-type, user-agent, host, and our signature headers) cannot be overridden.
Testing
To send a test event:- Open your endpoint from Settings → Webhooks
- Click Send test event
- Pick an event type from the dropdown
- Click Send
test.
Managing endpoints
From the endpoint detail page you can:- Edit subscribed events — change which events trigger this endpoint
- Edit description — update the human-readable label
- Edit custom headers — change static headers sent on every delivery
- Disable — stop firing without losing history (use the three-dot menu)
- Delete — remove the endpoint and its delivery history permanently
Troubleshooting
Why isn't my endpoint receiving events?
Why isn't my endpoint receiving events?
Verify the endpoint is Enabled, the events you expect are in Subscribed events, and the URL is reachable from the public internet. Use Send test event to confirm end-to-end connectivity. If real high-risk tool uses still don’t arrive, check that the user actually triggered a tool use with
risk_score ≥ 8 by looking at Terminal Runs.Signature verification fails on every event
Signature verification fails on every event
The most common cause is verifying against a parsed JSON object instead of the raw body bytes. Make sure your handler reads the raw request body before parsing. Also confirm you copied the full
whsec_… secret without truncation.My receiver is slow / times out
My receiver is slow / times out
Process the event asynchronously. Acknowledge with 200 immediately and queue the actual work — anything taking longer than 15 seconds will be treated as a failure and retried.
I want to replay an old event
I want to replay an old event
Delivery history is visible per-endpoint, but events are not currently replayable through the UI. Re-trigger the source action in your AI tool, or contact support to manually replay a specific delivery.
Tool Policies
Configure which actions trigger which event types
Slack
Pair webhooks with Slack approval workflows

