Skip to content

Webhooks

Asynchronous results are delivered to URLs you register. Registration is done by your own administrator in the dashboard rather than per request, because a URL we will post to is a URL we can be pointed at.

Your endpoint must be public HTTPS. We refuse private ranges, loopback and link-local addresses, and we re-check the address immediately before every delivery, so a hostname that stops resolving publicly stops receiving.

The signing secret is in the registration response and nowhere else.

Every POST carries:

HeaderValue
X-RV-Eventverification.completed or verification.failed
X-RV-DeliveryOur delivery id, stable across retries of that delivery
X-RV-TimestampUnix seconds
X-RV-SignatureHex HMAC-SHA256 of <timestamp>.<sha256 of the raw body>
import hashlib, hmac
def is_ours(raw_body: bytes, timestamp: str, signature: str, secret: str) -> bool:
message = f"{timestamp}.{hashlib.sha256(raw_body).hexdigest()}"
expected = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)

A delivered payload is filtered by your field groups, exactly as a synchronous response is.

  1. We try immediately, then after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours: seven attempts over about 33 hours.

  2. Any 2xx counts as delivered. Anything else is a failure, and we do not follow redirects.

  3. After the last attempt the delivery is marked dead and we stop. We do not retry forever: one unreachable receiver should not become an unbounded queue, and a result that is a week late is of no use to anyone.

Answer quickly and do the work afterwards. Our timeout is 10 seconds, and a receiver that does its processing inline will start timing out under load.

Terminal window
curl "https://api.randaverify.com/v2/webhooks/deliveries?status=dead" \
-H "Authorization: Bearer rv_live_..."

Requires usage:read. status may be pending, delivered or dead.

Terminal window
curl -X POST https://api.randaverify.com/v2/webhooks/deliveries/4821/replay \
-H "Authorization: Bearer rv_live_..."

A replay queues a new delivery of the same payload, so your record of what arrived and when is not rewritten. An afternoon of downtime is a handful of replays rather than a reconciliation by hand.

Results are always readable at GET /v2/verify/{reference} as well, so a webhook you never received is never a lost result.