Skip to main content

Signature verification

Tesouro can sign webhook deliveries with HMAC-SHA512 so you can verify a request came from Tesouro and wasn’t tampered with. Signing is opt-in per subscription - unsigned by default. Once enabled, every delivery to that subscription includes the three headers below. To enable signing for your subscription, contact your Tesouro implementation team.

Headers

HeaderExample valueDescription
x-tesouro-signaturet=1746673883,v1=A1B2C3...DEF0Signature header. v1=<hex> is the 128-character uppercase hex HMAC-SHA512 digest. t=<unix-seconds> is the send-time Tesouro stamped on the request, included in the signed input - use it for replay-window checks (see Replay protection).
x-tesouro-key-idprod-key-2026-01Identifier of the signing key Tesouro used. Use it to select the matching shared secret - Tesouro supports overlapping keys during rotation.
x-tesouro-algorithmhmac-sha512Algorithm used. Currently always hmac-sha512.

Computing the signature

signed_input = "{t}.{request_body}"
v1           = HEX( HMAC-SHA512( secret, signed_input ) )
{t} is the unix-seconds value from x-tesouro-signature. {request_body} is the raw request body bytes, byte-for-byte as received - don’t re-serialize or reformat the JSON before hashing. Compare case-insensitively (the digest is uppercase on the wire).

Verification steps

  1. Extract t=<unix-seconds> and v1=<hex> from the x-tesouro-signature header.
  2. Look up the shared secret for the key id in x-tesouro-key-id.
  3. Construct signed_input = "{t}.{raw_body}".
  4. Compute HEX(HMAC-SHA512(secret, signed_input)).
  5. Reject with 401 if the computed digest doesn’t match v1.
Example using openssl:
SECRET="<your shared secret>"
T="<unix-seconds value from x-tesouro-signature>"

EXPECTED=$( { printf '%s.' "$T"; cat request-body.json; } | openssl dgst -sha512 -hmac "$SECRET" -hex | awk '{print toupper($2)}')
echo "$EXPECTED"
If signing is enabled for your subscription, always verify the signature before processing the event.

Replay protection

After verifying the signature, also check that the request is fresh:
  1. Reject stale requests. Compare t=<unix-seconds> from x-tesouro-signature to the current time and reject anything older than a small window (Tesouro recommends 5 minutes).
  2. Deduplicate on deliveryId. Maintain a cache of recently-seen deliveryId values from the envelope and reject any request whose deliveryId you’ve already processed.

Secret rotation

Signing secrets can be rotated without downtime:
  • Add the new secret before removing the old one.
  • During the rotation period, accept requests signed by either key - use x-tesouro-key-id to select the matching secret per request.
  • Remove the old secret only after all in-flight webhooks finish.
Contact Tesouro support to rotate your webhook signing secret.

Endpoint validation

In addition to signature verification:
  • Register only HTTPS endpoints; plain HTTP isn’t supported.
  • Where possible, restrict your webhook handler to known Tesouro source IPs.
  • Return 401 for requests that fail validation.

Additional options

Tesouro also supports mutual TLS (mTLS) - contact the implementation team to enable it.