Webhooks
When a TREVEX admin approves a KYB request, a POST request is sent to the
webhook_url configured on your Project.
Delivery
Payload
Verifying signatures
Every webhook request is signed. You must verify the signature before trusting the payload - without this, an attacker who discovers your webhook URL can forge approval events.
How the signature is built
On every delivery, TREVEX computes:
and sends it hex-encoded in the X-Trevex-Signature header alongside the
X-Trevex-Timestamp header (Unix epoch seconds).
Headers
Verification steps
Check the timestamp
Reject the request if abs(now - timestamp) > 300 seconds. This prevents
replay attacks where an attacker captures a valid request and resends it
later.
Recompute the signature
Concatenate {timestamp}.{raw_body} and compute HMAC-SHA256 using your
webhook signing secret.
Use the raw request body - the exact bytes sent over the wire, not a
re-serialised version. Most frameworks let you access this via something like
request.body or request.rawBody. If you use a parsed JSON object and
JSON.stringify it, whitespace and key ordering will differ and verification
will fail.
Example implementations
Secret rotation
If you need to rotate your webhook signing secret, contact your TREVEX account manager. During the rotation window, TREVEX can sign with both the old and new secrets so you can switch without dropping events. Accept either signature until you’ve fully deployed the new secret, then ask TREVEX to retire the old one.
Webhook signing secrets are different from API keys. Losing or rotating one doesn’t affect the other. Store them separately in your secret manager.
Matching webhooks to your records
Use request_id to match an incoming webhook to the KYB request you submitted.
It’s the same value returned from create_claim.
trevex_company_id is the canonical TREVEX ID for the company. Store it if you
plan to call update_claim later.
Handling retries
Your endpoint should be idempotent - the same request_id may arrive more
than once if the first attempt fails or times out. A common pattern:
- Look up the
request_idin your database. - If already processed, return
200without doing any work. - Otherwise, process and store.
Return a 2xx status quickly (within the 10-second timeout). If you need to
do slow downstream work, queue it and return 200 immediately.
