Appearance
Outbound webhooks
Verity posts to your endpoints when a review changes state. This is how you react to decisions programmatically — there is no write API for them.
Setting one up
Settings → Webhook endpoints, needing webhook_endpoint.manage. You provide a URL and select the events to subscribe to; Verity generates a signing secret.
Events
| Event | Fired when |
|---|---|
entity_review.approved | A review is approved |
entity_review.rejected | A review is rejected |
entity_review.escalated | A review is escalated |
entity_review.escalated_to_aml | A review is escalated specifically to AML |
entity_review.assigned | A review's assignee changes |
entity_review.checks_queued | Checks are queued for a review |
An endpoint receives only the events it is subscribed to, and only while it is active.
Payload
http
POST https://your-endpoint.example.com/verity
Content-Type: application/json
X-Verity-Signature: sha256=<hex digest>json
{
"event_type": "entity_review.approved",
"entity_review_id": 7,
"entity_id": 42,
"account_id": 1,
"outcome": "approved",
"occurred_at": "2026-06-28T09:15:00Z"
}The payload identifies what happened to what. It deliberately does not carry the entity's full state — fetch GET /api/v1/entity_reviews/:id for that, so you read the current truth rather than a snapshot that may already be stale.
Verifying the signature
X-Verity-Signature is sha256= followed by the HMAC-SHA256 hex digest of the raw request body, keyed with your endpoint's signing secret.
ruby
expected = "sha256=" + OpenSSL::HMAC.hexdigest("SHA256", signing_secret, request.raw_post)
ActiveSupport::SecurityUtils.secure_compare(expected, request.headers["X-Verity-Signature"])python
import hmac, hashlib
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
hmac.compare_digest(expected, received_header)Three rules:
- Compute over the raw body, before any JSON parsing or re-serialising
- Use a constant-time comparison
- Reject anything whose signature does not verify — do not process it and log the mismatch
Delivery semantics
- Delivered by a background job, one per subscribed endpoint
- Any
2xxcounts as delivered; anything else is recorded as failed with the status and body - Attempts, timestamps and errors are visible under Webhook deliveries (
webhook_delivery.view)
There is no automatic retry
A failed delivery stays failed until someone acts on it. Do not treat webhooks as a guaranteed stream: if your system must not miss a decision, reconcile periodically against GET /api/v1/entities. Deliveries are recorded, so a gap is visible rather than silent.
Endpoint requirements
- HTTPS. Plain
httpworks only where your network genuinely permits it, and you should not. - Respond quickly with
2xxand do the work asynchronously — a slow endpoint is a failed one. - Be idempotent. Handle the same
entity_review_idandevent_typearriving twice.
Inbound webhooks
Verity also receives webhooks — Focal posts screening results to POST /webhooks/focal. Those are HMAC-authenticated and fail closed: if no secret is configured, the request is rejected rather than trusted. That is provider configuration, not something you set up as a consumer.