Webhook Integration
Request Authentication
How Token Vault authenticates webhook requests using HMAC-SHA256 signatures with replay prevention.
Signature Flow
Loading diagram...
Every request from Token Vault (except /v1/exchange) includes three headers for authentication and replay prevention:
X-TokenVault-Signature: sha256=<HMAC-SHA256 hex digest>
X-TokenVault-Timestamp: <Unix timestamp in seconds>
X-TokenVault-Request-Id: req_<12-char hex>
Content-Type: application/jsonSignature Verification
The signed payload is {timestamp}.{json_body}, HMAC-SHA256'd with the shared secret established during /v1/exchange.
# 1. Extract headers
signature = request.headers["X-TokenVault-Signature"] # "sha256=abc123..."
timestamp = request.headers["X-TokenVault-Timestamp"] # "1708300000"
raw_body = request.body # raw JSON bytes
# 2. Compute expected signature
signing_payload = f"{timestamp}.{raw_body.decode('utf-8')}"
expected = hmac_sha256(hmac_secret, signing_payload.encode("utf-8")).hexdigest()
# 3. Compare using constant-time comparison
if not constant_time_compare(expected, signature[7:]): # strip "sha256=" prefix
return 401 # Reject: signature invalidUse raw bytes for verification
Token Vault serializes the request body as compact JSON with no whitespace (json.dumps(body, separators=(",", ":"))). Your HMAC verification must use the raw bytes received. Do not re-serialize or pretty-print the body before computing the signature.
Replay Prevention
- Timestamp check: Reject requests where the timestamp is more than 5 minutes from the current time.
- Request ID deduplication: Track
X-TokenVault-Request-Idvalues to prevent duplicate processing.
Exceptions
Two endpoints do not require HMAC authentication:
POST /v1/exchange- Called before the HMAC secret exists. Authentication is via the one-time exchange code.GET /v1/health- Unauthenticated GET requests are allowed for external monitoring, load balancers, and the/bindstatus page. ThePOST /v1/healthvariant remains HMAC-signed.
All other endpoints must verify the HMAC signature.