webhook and no notification is sent — you would then have to poll.
The request
chmod sends aPOST with two headers that let you prove the call came from us:
The body identifies the transaction and its new status.
Verifying the signature
chmod signs with a private key and publishes the matching public key as a JWKS document:1
Read both headers
Reject the request outright if
X-Timestamp or X-Signature is missing.2
Rebuild the signed string
Concatenate the timestamp, a colon, and the transaction id from the body:
{timestamp}:{transactionId}. No spaces, no trailing newline.3
Verify against the public key
Fetch the JWKS (cache it — do not fetch per request) and verify
X-Signature over that string. The alg in the JWKS entry tells you which algorithm to use.4
Check the timestamp is recent
Reject anything older than a few minutes. A valid signature stays valid forever, so without this check a captured request can be replayed indefinitely.
Responding
Return2xx as soon as you have accepted the call. Do the real work — fetching the transaction, updating your records, notifying the user — out of band.
A slow handler is a fragile handler: if your endpoint blocks on a database write and times out, the delivery counts as failed even though you received it.
Delivery and idempotency
Assume at-least-once delivery. A retry after a network hiccup means the same status can reach you twice, so make your handler idempotent — key ontransaction_id and the status, and make a repeat a no-op
Not receiving calls?
When a webhook is genuinely lost, polling is the fallback — which is why a reconciliation job over transactions that never reached a terminal status is worth having in production.

