Streams webhooks are signed HTTP POSTs from ClutchCall to a URL you own. You manage them on the Webhooks screen, which is wired to the unified webhook plane (webhooks.*) with vertical: 'streams'. Each row in the table is one webhook_endpoint: a URL, an event-type subscription, a signing secret, and a health state. This page explains what the event picker on that screen is actually storing and how the dispatcher interprets it. Signing, retry/backoff behaviour, and refused destinations are covered on the platform webhooks page.

Event types

The picker is catalog-driven. The console calls webhooks.catalog and reads streams.groups from the returned registry; each entry is a group of concrete event-type tokens: The authoritative list of Streams event types is therefore whatever the registry serves your org — the picker renders exactly that set and nothing else. This is deliberate: the screen replaced a free-text event field that happily stored typos which no producer ever emitted. The server enforces the same rule, so subscriptions created through the API cannot drift either: webhooks.update runs assertKnownEventTypes on any eventTypes you send and rejects unknown tokens. Groups exist only in the UI. What lands on the endpoint row is a flat array of patterns.

How event patterns are matched

The dispatcher matches a fired event type against the endpoint’s stored patterns using three forms: The console applies the identical rule in reverse when you open Edit: a group’s checkbox is pre-checked only when every type in that group is covered by the row’s stored patterns. A row stored as * therefore opens with all groups checked.

Choosing groups vs ’*’

What the forms store depends on how many groups you check:
  • All groups checked collapses to ['*']. The endpoint then also receives event types that are added to the registry after you saved it. The dialog says so under the checkbox list.
  • Some groups checked stores the concatenated types of those groups as an explicit list. New event types added to the registry later are not delivered to that endpoint until you re-open Edit and save again.
  • No groups checked resolves to an empty array, which fails validation with “Pick at least one event group”. An empty subscription is never silently turned into “everything”.
Pick * when your handler switches on the event type and ignores what it does not recognise. Pick individual groups when your handler is strict, or when you want to keep delivery volume to one endpoint narrow.

Endpoint URL rules

By default an endpoint URL must be https://. Ticking On-prem / private-network endpoint at create time sets allowPrivateEgress, which permits http:// and RFC1918 targets for that row. The flag is stored on the row and is what the server consults afterwards. webhooks.update loads the existing endpoint and rejects an http:// URL with http:// endpoints require private egress (on-prem targets only) unless the row was created with private egress; the URL is then re-checked for deliverability before the patch is applied. The Edit dialog mirrors this, so a row created as public-only will not let you paste an http:// URL. The optional Description is free text (max 300 characters) and is shown under the URL in the table. Use it to record where deliveries land.

Signing secret and rotation

Each endpoint has its own HMAC secret (whsec_…). It is shown once — in the dialog that appears right after create, and again after a rotate. The stored row keeps only a sealed value plus a short preview, so there is no way to read the secret back later. Set the secret in your endpoint and verify the signature header on every delivery; the secret dialog names the exact header. Rotate mints a new secret and the previous one stops verifying immediately — there is no overlap window. If your endpoint validates signatures, update your side first, then rotate, or accept a gap in which deliveries fail signature checks.

Testing an endpoint

Test calls webhooks.sendTest, which enqueues a signed test.ping envelope directly to that endpoint. The test bypasses the subscription: it is delivered regardless of which groups you checked. The procedure then waits for the delivery worker’s verdict, polling the delivery row for up to 12 seconds, and reports one of: A test is audit-logged with its ok flag and status code.

Endpoint health states

The Health column is derived, not stored as a single field: A brand-new endpoint is Untested, not green. Green is earned by an actual delivery — send a test if you want to confirm the URL before events start flowing. The Last status column shows the most recent HTTP status code, or — if there has never been one.

Pausing, resuming, and deleting

Pause flips the row to status: 'paused' through webhooks.update; the endpoint receives no deliveries until it is resumed. Resume sets status: 'active' and, in the same patch, clears consecutive_failures — so resuming also clears an endpoint that the platform auto-paused for repeated failures. Delete soft-deletes the endpoint. Deliveries to it stop immediately and the action cannot be undone. Endpoint create, update, secret rotation, and test are all recorded in the org audit log.

Where deliveries are listed

This screen manages endpoints only. Individual deliveries — their payloads, statuses, and response codes — live on the Request logs page (/logs). webhooks.list backs that view: it resolves the org’s streams endpoints (plus org-wide rows whose vertical is null) and returns delivery rows for them, newest first, optionally filtered by delivery status (pending, delivering, delivered, dead). Full payloads are fetched separately for the detail drawer rather than in the list.

Plan entitlement

Add endpoint is disabled when the org’s plan does not include the Streams product. The button explains this on hover; existing endpoints remain visible.