License
Webhook endpoints and the billing pull APIs are gated by thewebhookEndpointsEnabled flag on your enterprise license. Without it, the REST surfaces answer 403, delivery does not register, and Settings > Webhooks shows the upgrade state. Adding the license to a running deployment lights the surfaces up without a migration, the same flow as every enterprise flag (see Compliance).
Outbound egress and SSRF policy
Webhook URLs are customer-supplied and fired from your control-plane workers, so the sender is fenced:- HTTPS only. Plain
http://URLs are rejected at save time. - Private and loopback addresses are always blocked, including IP literals and bracketed IPv6, regardless of the deployment-wide
BLOCK_LOCAL_HTTP_CALLSsetting. A delivery must never reach10.x,192.168.x,localhost, or link-local addresses. - Redirects are never followed. A
3xxfrom a receiver is a terminal delivery failure, not a hop. - Response sizes and timeouts are capped; the receiver’s status, latency, and a response excerpt land in the delivery log.
Local and internal receivers
Development setups and some internal architectures legitimately need receivers on private addresses. The escape hatch:http:// and resolve to private or loopback addresses. Only the local-address fence is relaxed: size caps, timeouts, no-redirects, and signing all stay in force. Leave this unset in production unless your receivers are deliberately internal; it is the difference between “our workers can only call out” and “our workers can reach anything on the pod network”.
Authentication model
Webhook and billing REST surfaces authenticate with an organization API key and organization-tier permissions (webhookEndpoints:view|manage, gatewaySpend:view|manage). These permissions are organization-exclusive: a team- or project-scoped role binding can never grant them, so a project-level credential cannot register an endpoint that streams org-wide events out. Grant them through org roles or a custom org-scoped role; see RBAC.
Billing settlement window
A gateway request that was admitted but never confirmed is settled after a grace window and delivered asgateway.request.settled with unknown cost (see settled semantics). The default grace is 30 minutes. Tune it with:
Retention
- Spend records: fixed 13 months, declared in the table’s own migration, deliberately outside both tenant retention policies and the cold-storage TTL reconciler. Do not add
gateway_spendto retention configs; a unit test pins the exemption. - Delivery log: 30 days, matching the automations webhook log.
See also
- Webhooks: the platform reference.
- Billing & spend events: the money contract and reconciliation.
- Data retention: tenant retention and the billing exemption.
- Self-hosting security: network boundaries and secrets.