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.
Registering
Section titled “Registering”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.
Verifying a delivery
Section titled “Verifying a delivery”Every POST carries:
| Header | Value |
|---|---|
X-RV-Event | verification.completed or verification.failed |
X-RV-Delivery | Our delivery id, stable across retries of that delivery |
X-RV-Timestamp | Unix seconds |
X-RV-Signature | Hex 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.
What we do when your server is down
Section titled “What we do when your server is down”-
We try immediately, then after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours: seven attempts over about 33 hours.
-
Any
2xxcounts as delivered. Anything else is a failure, and we do not follow redirects. -
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.
Seeing and replaying what you missed
Section titled “Seeing and replaying what you missed”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.
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.