Skip to main content
This page collects the patterns most webhook integrations need: verifying signatures before processing, deduplicating redelivered events, and routing events to the right handler.

Signature Verification

Every webhook request from Paxos Labs includes a cryptographic signature that lets you confirm the request is authentic and hasn’t been tampered with. Always verify signatures before processing webhook payloads.

Headers

Each webhook POST request includes these headers:

Signature Scheme

X-PAXOS-LABS-SIGNATURE contains the raw hex-encoded HMAC-SHA256 digest. The timestamp used in the HMAC computation is sent in the separate X-PAXOS-LABS-TIMESTAMP header.

Verification Algorithm

1

Read the timestamp and signature headers

Read X-PAXOS-LABS-TIMESTAMP for the RFC 3339 timestamp and X-PAXOS-LABS-SIGNATURE for the hex signature.
2

Check the timestamp

Reject requests where the timestamp is more than 5 minutes from your server’s current time. This protects against replay attacks.
3

Construct the signed payload

Concatenate the timestamp, a literal period (.), and the raw request body (the exact bytes received — do not parse and re-serialize):
4

Compute the expected signature

Calculate an HMAC-SHA256 using your endpoint’s signing secret as the key and the constructed string as the message. Hex-encode the result.
5

Compare signatures

Use a constant-time comparison to check that your computed signature matches the v1 value from the signature header. This prevents timing attacks.

Implementation Examples

Use timingSafeEqual instead of === to prevent timing side-channel attacks.

Best Practices

Never process a webhook payload without verifying the signature first. An unverified payload could be forged by a malicious actor.
Standard string comparison (===, ==) leaks timing information that attackers can exploit. Always use timingSafeEqual (Node.js), hmac.compare_digest (Python), or hmac.Equal (Go).
Reject events where the timestamp is more than 5 minutes from your server’s clock. This prevents captured requests from being replayed later.
Compute the HMAC over the exact bytes received in the HTTP body. Parsing to JSON and re-serializing can change whitespace or key ordering, producing a different signature.
Keep your signing secret in a secrets manager or encrypted environment variable — never hard-code it or commit it to source control.
Return a 2xx status code within 10 seconds. Move heavy processing to a background queue so the webhook handler returns immediately.
Use the event id field to deduplicate. Store processed event IDs and skip any that you’ve already handled.

Troubleshooting


Idempotent Processing

Events may be delivered more than once. Use the id field to deduplicate:
In production, store processed event IDs in a database rather than in-memory to survive restarts.

Correlating Deposit Lifecycle Events

id deduplicates deliveries of a single event; it changes between events. To connect the events of one deposit — pending through confirmed/failed and finalized — key your state on deposit_id, which stays constant across the whole sequence. Deliveries can arrive out of order, so guard against a late-arriving earlier stage overwriting a later one:
See the Smart Deposit Routing Events reference for the full lifecycle.

Type-Based Routing

Route events to different handlers based on the type field: