Every modality emits events through the same webhook system. Voice, chat, streams, realtime, tunnel, QuickDesk, VPN, teleop, crypto, robotics, inference, compute and SMS all register endpoints the same way, sign the same way, and retry the same way. Learn it once. You register an endpoint in the console of the product you want events from. Each console’s Webhooks screen manages endpoints for its own vertical.
This page is the platform mechanism — signing, retries, the delivery log. For the voice event catalogue specifically (what voice.call.* means and when each fires), see Voice webhooks.

Choosing event types

Every event type is a <vertical>.<subject>.<action> token, and the set of valid tokens is a server-side registry. The picker in each console renders from that registry, so what you can select is exactly what something can actually send. You can subscribe to * for everything in the vertical, or use dot-bounded globs such as voice.call.*.
An unknown token is rejected when you save, not silently stored. That is deliberate: a typo used to be accepted and then simply never matched, which looks identical to “the event never fired”. If you get unknown event type, the token does not exist — check the picker rather than guessing.

Verifying the signature

Every delivery carries three headers:
These header names are literal, on every brand. They are not brand-swapped, so do not substitute your own brand name — match on X-Clutchcall-Signature exactly or your verification will never find the header.
The signature is Stripe-style: the timestamp is bound into the MAC, so a captured request cannot be replayed later with a fresh timestamp.
Two rules decide whether your verification works:
  1. HMAC the raw bytes. Parse the JSON after verifying. If you decode and re-serialize first, key order and spacing change and the MAC will not match.
  2. Compare in constant time, and reject a timestamp that is too far from now — that check is what makes the replay binding useful.
The signing secret is whsec_…. It is minted server-side, stored sealed, and shown in cleartext exactly once — when you create the endpoint or rotate the secret. There is no way to read it back afterwards; if you lose it, rotate.
Realtime endpoints additionally receive X-Pusher-Signature, an HMAC over the body alone, so an existing Pusher webhook consumer keeps working unchanged. There is no X-Pusher-Key — a unified endpoint is not bound to a single app key.

Retries

A delivery is attempted up to 6 times. The schedule, with ±20% jitter: Return 2xx to acknowledge. Anything else — including a timeout, and the request timeout is 10 seconds — counts as a failure and schedules a retry. After 20 consecutive failures the endpoint auto-pauses and its status becomes failing. That is a circuit breaker, not a deletion: nothing is retried against a dead endpoint until you resume it from the console. Fix the receiver, then resume. Because retries exist, your handler must be idempotent. Deduplicate on X-Clutchcall-Delivery, which is stable across attempts of the same delivery.

When something does not arrive

Each console’s Webhooks screen has a delivery log showing status, attempt count, the last response code, the last error, and when the next attempt is due. Use it before assuming an event never fired — the common answers are that the endpoint returned a non-2xx, or that it auto-paused after a run of failures. You can redeliver any past delivery from that log, and send a test event to an endpoint without waiting for real traffic.

Endpoint URLs we refuse

A webhook URL must be http(s) and must resolve to a routable address. Link-local, cloud metadata and loopback addresses are refused unconditionally; private RFC1918 ranges are refused unless the deployment is on-prem and enables it. The check runs twice — when you save, so you see the error in the form, and again at send time, so a hostname repointed after creation still cannot reach inward.