Webhooks
Webhooks let your systems react to events happening inside Agent Handler. Today Agent Handler emits five events: a Registered User is created, a Registered User is deleted, a third party sends an inbound webhook that Agent Handler forwards to you, an employee submits a tool access request, and a tool access request is approved or denied. Every outbound webhook is signed, so your endpoint can confirm it came from Agent Handler before trusting the payload.
Outbound webhooks are events from Agent Handler to you, the common case. Inbound webhooks are events from a third party (Slack, Jira, GitHub) to Agent Handler, then forwarded to your outbound webhook so you get one unified event stream.
Outbound webhooks
Setting up a subscription
- Open Connectors → Webhooks.
- Click Add webhook.
- Enter your HTTPS callback URL.
- Select the events to subscribe to (see catalog below).
- Save.
Your endpoint should accept POST requests, parse JSON, and return 2xx on success. A non-2xx response, or a response slower than the timeout, is recorded as a failed delivery (see Delivery and failures).
For local development, webhook.site gives you a temporary URL that captures requests in a browser. Once you’ve confirmed payloads look right, point at your real endpoint.
Event catalog
Agent Handler emits five event types. This table is the complete set.
The two tool access request events are set up in Workforce organization settings rather than with the steps above; Tool access request events covers where.
There are no tool-call, credential, or security-rule webhook events today. Tool-call activity and security violations are visible in the Tool Call Logs and Violations and alerts dashboards, and can be pulled via the logs API.
Payload shape
Every payload shares the same envelope: a hook object with delivery metadata, a data object whose shape depends on the event, and an origin object that is populated only for inbound events.
Tool access request events
TOOL_ACCESS_REQUEST_CREATED and TOOL_ACCESS_REQUEST_REVIEWED let you route employee tool access requests into the approval system you already run, such as a ticketing workflow or an on-call bot, instead of watching the Requests page. They are set up somewhere different from the other events: in Workforce, open Settings → Organization and add your endpoint under Request webhooks, in the Tool access requests section. That section appears once tool access requests are turned on. Deliveries are signed with the same verification key and follow the same delivery rules as every other outbound webhook.
TOOL_ACCESS_REQUEST_CREATED fires when an employee submits a request. Submitting an identical request while the first is still pending does not fire a second event. TOOL_ACCESS_REQUEST_REVIEWED fires when a request is approved or denied, and on nothing else.
Revoking an approval, an employee canceling their own request, and an approval expiring fire no event. A revocation or a lapsed grant has to be picked up from the API.
Delivery is best effort. A delivery that errors or takes longer than the 2-second timeout is dropped and not replayed (see Delivery and failures). If you cannot afford to miss a request, reconcile on a schedule against GET /api/v1/tool-access-requests/?status=pending, described in Reviewing tool requests with the API.
A TOOL_ACCESS_REQUEST_REVIEWED event for a request approved from the dashboard:
A TOOL_ACCESS_REQUEST_CREATED event has the same shape, with status set to pending, approved_tool_slugs empty, decision_note empty, and approved_expires_at, reviewed_at, and reviewed_by all null. hook.event_timestamp is when the request was submitted for TOOL_ACCESS_REQUEST_CREATED, and when it was approved or denied for TOOL_ACCESS_REQUEST_REVIEWED.
The two events close a round trip. A TOOL_ACCESS_REQUEST_CREATED event carries who asked, for which tools, and why, which is enough for your system to decide. Send the decision back with PATCH /api/v1/tool-access-requests/{data.id}/, described in Reviewing tool requests with the API, and the result arrives as a TOOL_ACCESS_REQUEST_REVIEWED event with reviewed_by.type set to api.
Verifying the signature
Every outbound webhook is signed with HMAC-SHA256 using a secret you control. The signature is in the X-Webhook-Signature header, hex-encoded. Verify it before trusting the payload, since anyone can POST to a public URL.
Get your signing secret at Webhooks → Verification key. Rotate it from the same page; rotation invalidates all signatures generated with the old key.
The signature is computed over the raw request body exactly as sent. If your framework parses JSON before you see the bytes, configure it to give you the raw body; re-stringifying parsed JSON will produce a different signature.
Delivery and failures
Agent Handler sends each event with a 2-second timeout. A response slower than 2 seconds, a non-2xx status, or a connection error is recorded as a failed delivery.
Failed deliveries are not retried, and there is no replay from the dashboard today. A delivery that fails is not sent again. Build for this:
- Return
2xxas fast as you can and do the real work asynchronously. Holding the connection open to finish processing will blow past the 2-second window and the event is lost. - If you need a guarantee that you’ve seen every Registered User, reconcile against the Registered Users API on a schedule rather than relying on the webhook alone. Treat webhooks as a low-latency signal, not the system of record.
Idempotency
Deduplicate on hook.event_id. hook.id identifies the subscription, not the event, so it repeats on every delivery. Because deliveries aren’t retried, duplicates are unlikely, but one event does reach every subscription that includes it with the same event_id, so if two subscriptions point at the same endpoint, event_id is how you tell a second copy from a new event.
Inbound webhooks
Some Connectors (Slack, Jira, GitHub) emit webhooks to subscribers. Agent Handler can be that subscriber, then forward the event to your outbound webhook as an INBOUND_WEBHOOK_RECEIVED event. You get a single, unified event stream regardless of how many third parties you’re reacting to.
Inbound webhooks are passthrough only. Every inbound subscription must be paired with an outbound subscription that includes INBOUND_WEBHOOK_RECEIVED.
Setup
- Subscribe an outbound webhook to
INBOUND_WEBHOOK_RECEIVED. Add the event to an existing outbound subscription (or create a new one) at Connectors → Webhooks. - Enable inbound webhooks for the Connector. Open the Connector’s detail page; if it supports inbound webhooks, you’ll see a Webhook section. Click Add webhook.
- Connector-specific configuration. Each third party requires different credentials. Slack wants the app’s signing secret; GitHub wants a webhook secret you generate; Jira wants admin access to register the webhook with the third party.
Each Connector’s webhook setup is documented inline in the dashboard. The Slack flow, as a representative example:
- In the Slack app dashboard, copy the signing secret from Basic Information → App Credentials.
- Paste it into the Slack Connector’s webhook field in Agent Handler.
- Agent Handler returns a webhook URL, copy it.
- In Slack, go to Event Subscriptions → Request URL, paste the URL, and select the events you want forwarded.
- Slack verifies the URL automatically. Once verified, events flow Slack → Agent Handler → your outbound endpoint as
INBOUND_WEBHOOK_RECEIVEDpayloads.
Inbound payload shape
For an inbound event, data holds the forwarded third-party body and origin holds the original request Agent Handler received from the third party (method, URL, headers, and body), along with the Connector and Registered User it belongs to. hook.third_party_data_event_type carries the third party’s own event name when the Connector can determine it.
Next
Tag tool calls with session or workflow metadata using Custom headers for MCP.