X-Signature header so you can confirm it came from Etherfuse and nobody changed it.
How it works
When you create a webhook subscription, the response includes asecret: a 32-byte HMAC key encoded as URL-safe base64. Store it securely; we return it only once.
We sign each delivery with that secret:
- We canonicalize the JSON body with RFC 8785 JCS: sorted keys, no extra whitespace.
- We compute HMAC-SHA256 over the canonicalized string, keyed with your secret decoded from URL-safe base64.
- We send the result as
X-Signature: sha256={hex}, with the digest in lowercase hex.
The secret uses the URL-safe base64 alphabet (RFC 4648 section 5:
- and _ in place of + and /), with = padding. Decode it with a URL-safe decoder. A standard base64 decoder rejects secrets that contain - or _, or decodes them to the wrong key, and your signatures stop matching.Verifying the signature
- Parse the received body as JSON and canonicalize it.
- Decode your secret from URL-safe base64.
- Compute HMAC-SHA256 over the canonicalized string.
- Compare
sha256={hex_result}to theX-Signatureheader with a constant-time comparison.
Rust
Node.js
Python
Use a library that implements RFC 8785 (JCS). A plain
json.dumps(body, sort_keys=True, separators=(",", ":")) is close but does not follow every JCS rule.Delivery and retries
- Return any
2xxto acknowledge a delivery. - We make up to 3 attempts per delivery, 5 seconds apart. We try again after a connection error, a timeout, a
5xx, a408, a429, or any other non-2xxstatus not in the next item. - We stop after the first response when your endpoint returns
400,401,403,404,405,410or422. A handler that returns401for a bad signature gets no retry. - We do not send an abandoned delivery later. Read the object with its
GETendpoint to recover the current state.