Webhooks
Configured per tenant in the console, behind the operate ability. One or more https destinations, each subscribed to the event types it wants.
Events
| verification.completed | An asynchronous verification finished, or failed. |
| alert.raised | A rule fired on a filed reading and a case is waiting for somebody to look at it. A signal, never a verdict: the payload names the rule and the alert, and the console is where it is closed. |
Each event is delivered once per occurrence: an evaluation that runs again does not deliver the same alert twice.
What a payload never contains
Never a capture id, never a core hash, never a file name. The platform keeps no index from media — a file, a core hash — to anything, and a stable per-capture identifier pushed out of it on a schedule would hand a receiver the correlation the platform refuses to build for itself. Correlate on the opaque job id you were handed when you asked for the verification. That id expires within the hour on both sides, and we do not keep the mapping for you.
Verifying a delivery
Vcap-Event: verification.completed
Vcap-Delivery: <event id, stable across retries>
Vcap-Signature: t=<unix seconds>,v1=<hex>[,v1=<hex>]
The signed message is fixed and domain-separated, so a receiver in any language rebuilds it byte for byte: "vcap/1.0/webhook" ‖ "\n" ‖ t ‖ "." ‖ raw body, HMAC-SHA256 under your endpoint's secret.
expected = OpenSSL::HMAC.hexdigest("SHA256", secret, "vcap/1.0/webhook\n#{t}.#{body}")
Check t against your own tolerance — five minutes is what our checker uses — and compare in constant time. More than one v1= means a rotation is in progress: accept if any of them matches.
What that signature proves, and what it does not
It proves the delivery was made by somebody holding your endpoint's secret, and that the body arrived byte for byte as it was signed. It is a shared secret, not a public-key signature: it is not evidence you could show a third party, because your own receiver could have produced it. There is no key management service and no hardware behind it, so treat it as you would any other credential you hold — one secret per endpoint is what bounds the damage a leaked one can do. Rotating keeps the previous secret signing for 24 hours so you can update your receiver without dropping deliveries.
And a delivery is never evidence about a file. Nothing in a webhook is a verdict you can rely on as proof — the proof is the sealed file, and it verifies without us.
Retries, and giving up
A non-2xx, a refused connection or a 5 s timeout is a failure. Retries back off — 1, 2, 4, 8, 16, 32 and 64 minutes: eight attempts, seven waits, a little over two hours from the first try to the last. The eighth failure schedules nothing; the delivery reads exhausted and stops. Retrying a dead URL forever is indistinguishable from having lost the event. The console shows each delivery's attempt count and last result, and an exhausted delivery can be retried by hand with the same bytes — Vcap-Delivery is stable across attempts, so deduplicate on it.