Scoping & Event Filtering
By default every webhook endpoint you configure is account-wide: it receives events from every agent on your account. Two controls narrow that down.- Scope — bind an endpoint to a single agent, so it receives only that agent’s events.
- Event allowlist — subscribe an endpoint to specific event types, so everything else is filtered out.
Account vs agent scope
An endpoint created withoutagent_id is account-wide. Pass agent_id at
creation to bind it to one agent:
POST /api/v2/agent/{agent_id}/webhooks does the same thing with the scope taken
from the path; any agent_id in the body is ignored there. Edit, delete, rotate,
test, and delivery history stay on /config/webhooks/{id} for both — an endpoint
id is already unambiguous.
An agent_id that is not one of your own agents — or belongs to a deleted agent
— is rejected with 404 NOT_FOUND, not 403, so this route cannot be used to
probe whether an agent id exists on someone else’s account.
Limits are per scope; labels are per account
These two are counted differently, and it is easy to assume otherwise.Deleting an agent does not delete its endpoints.
DELETE /api/v2/agent/{id}
is a soft delete, so the agent’s endpoint rows survive it. They keep holding
their labels and keep counting against that agent’s 5, while
GET/PATCH /agent/{id}/webhooks* start returning 404 NOT_FOUND because the
agent is gone.Delete the endpoints yourself with DELETE /config/webhooks/{id} — that route
works on an endpoint whose agent has been deleted — before you retire an agent,
or you will strand labels you cannot reuse.Agent-scoped endpoints are additive
An agent-scoped endpoint does not replace your account-wide ones. It adds to them. Given:
…a
session.completed on agent a1b2c3d4… produces two independent
deliveries — one to prod, one to acme-tenant. Each has its own signature,
its own retry schedule, and its own row in delivery history. One failing does
not affect the other.
An event on any other agent reaches only prod.
Turning off account-wide fan-out
Additive is the right default, but not always what you want. An agent that feeds a different downstream system — a white-label tenant, a client migrating from another platform, a one-off integration — should be able to stop duplicating its traffic into your main integration.webhooks_include_account_endpoints is a per-agent flag, true by default:
The response echoes the stored value:
endpoints lists only this agent’s own endpoints — deliberately, so you can
see which rows actually live on the agent. It does not fold in the account-wide
ones even when include_account_endpoints is true. available_events is the
live event catalog; read it from here rather than hard-coding the list.
The flag only applies to events that carry an agent. An event with no agent
attached always goes to your account-wide endpoints — honouring a stray
false there would disable webhooks account-wide.The guard: 409 NO_AGENT_ENDPOINT
Setting the flag to false is refused while the agent has no enabled
endpoint of its own:
true rather
than leaving the agent with nowhere to deliver. The bias is deliberate — an
unexpected duplicate is an annoyance you can dedupe, a dropped event is
unrecoverable.
Event allowlist
events is a per-endpoint allowlist. Omit it, or send [], and the endpoint
receives every event type — including ones added to the platform later.
400 VALIDATION_ERROR listing the valid
set, so a typo fails loudly at configuration time rather than silently
delivering nothing. Duplicates are de-duplicated rather than rejected.
Event catalog
Six event types, and this is the complete list.webhook.test is deliberately not in this list. It is fired only by the
test endpoint and always reaches the endpoint being tested, regardless of its
allowlist — otherwise “send test” would silently do nothing on exactly the
endpoints most likely to be misconfigured. You still receive it, so handle or
ignore it explicitly.
How the two controls combine
Both filters must pass for a delivery to happen:- Scope decides which endpoints are candidates for this event.
- Allowlist decides whether each candidate accepts this event type.
events array — the default — receives every type, so the interaction never
arises.
Worked example. Agent a1b2c3d4… has include_account_endpoints: false and one
endpoint subscribed to ["session.completed"]:
To receive
call.transferred too, either add it to the endpoint’s events, add
a second agent endpoint that subscribes to it, or turn
include_account_endpoints back on.
Webhooks vs lifecycle hooks
These are different mechanisms and people reach for the wrong one.
If you want to know something happened, use a webhook. If you want to fetch
or push data as part of the call, use a lifecycle hook.
Next steps
Webhook Setup
Create endpoints, verify signatures, and send test events.
Webhook Payloads
Full payload structure for each event type.