Webhooks

Get notified when a KYB request is approved

When a TREVEX admin approves a KYB request, a POST request is sent to the webhook_url configured on your Project.

Delivery

PropertyValue
MethodPOST
Content-Typeapplication/json
SigningX-Trevex-Timestamp + X-Trevex-Signature headers on every request
Timeout10 seconds
Retries3 attempts at 60 s -> 300 s -> 900 s (exponential backoff)
SuccessAny 2xx HTTP status
AuditEvery attempt is logged in WebhookDeliveryLog

Payload

claim_approved
1{
2 "event": "claim_approved",
3 "request_id": "REQ-ABC123XYZ789",
4 "trevex_company_id": "TRXAE0000042",
5 "company": {
6 "name": "Acme Corporation LLC",
7 "country_code": "AE",
8 "trade_license_url": "https://...",
9 "industry": {
10 "sector": "Technology",
11 "group": "Software",
12 "industry": "SaaS",
13 "sub_industries": ["Cloud Computing", "Artificial Intelligence"]
14 },
15 "address": {
16 "line_1": "123 Sheikh Zayed Road",
17 "city": "Dubai",
18 "country": "UAE"
19 },
20 "authorized_signatory": {
21 "name": "John Doe",
22 "designation": "CEO",
23 "email": "john@acmecorp.com",
24 "mobile": "+971501234567",
25 "nationality": "AE"
26 }
27 },
28 "contact_email": "john.doe@example.com"
29}

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:

signature = HMAC_SHA256(
secret = your_webhook_signing_secret,
message = "{timestamp}.{raw_request_body}"
)

and sends it hex-encoded in the X-Trevex-Signature header alongside the X-Trevex-Timestamp header (Unix epoch seconds).

Headers

HeaderExample
X-Trevex-Timestamp1745600123
X-Trevex-Signature7c3f2a1b9e4d... (hex-encoded HMAC-SHA256, 64 characters)

Verification steps

1

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.

2

Recompute the signature

Concatenate {timestamp}.{raw_body} and compute HMAC-SHA256 using your webhook signing secret.

3

Constant-time compare

Compare your computed signature to X-Trevex-Signature using a constant-time comparison function (not ==). This prevents timing attacks.

4

Only then, parse the body

Never parse or act on the payload before verification succeeds.

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

1import hmac
2import hashlib
3import time
4from flask import Flask, request, abort
5
6app = Flask(__name__)
7WEBHOOK_SECRET = b"your_webhook_signing_secret"
8TOLERANCE_SECONDS = 300
9
10def verify_trevex_webhook(raw_body: bytes, timestamp: str, signature: str) -> bool:
11 # 1. Timestamp window
12 try:
13 ts = int(timestamp)
14 except (TypeError, ValueError):
15 return False
16 if abs(time.time() - ts) > TOLERANCE_SECONDS:
17 return False
18
19 # 2. Recompute
20 message = f"{timestamp}.".encode() + raw_body
21 expected = hmac.new(WEBHOOK_SECRET, message, hashlib.sha256).hexdigest()
22
23 # 3. Constant-time compare
24 return hmac.compare_digest(expected, signature)
25
26@app.post("/webhooks/trevex")
27def trevex_webhook():
28 raw = request.get_data() # raw bytes, not request.json
29 ts = request.headers.get("X-Trevex-Timestamp", "")
30 sig = request.headers.get("X-Trevex-Signature", "")
31
32 if not verify_trevex_webhook(raw, ts, sig):
33 abort(401)
34
35 event = request.get_json()
36 # handle event["request_id"], event["trevex_company_id"], ...
37 return "", 200

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:

  1. Look up the request_id in your database.
  2. If already processed, return 200 without doing any work.
  3. 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.