Skip to main content
Every webhook delivery includes an 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 a secret: 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:
  1. We canonicalize the JSON body with RFC 8785 JCS: sorted keys, no extra whitespace.
  2. We compute HMAC-SHA256 over the canonicalized string, keyed with your secret decoded from URL-safe base64.
  3. 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

  1. Parse the received body as JSON and canonicalize it.
  2. Decode your secret from URL-safe base64.
  3. Compute HMAC-SHA256 over the canonicalized string.
  4. Compare sha256={hex_result} to the X-Signature header with a constant-time comparison.
We compute the signature over the canonicalized JSON, not the raw request body. Canonicalize before you compare, or the signature does not match.

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 2xx to 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, a 408, a 429, or any other non-2xx status not in the next item.
  • We stop after the first response when your endpoint returns 400, 401, 403, 404, 405, 410 or 422. A handler that returns 401 for a bad signature gets no retry.
  • We do not send an abandoned delivery later. Read the object with its GET endpoint to recover the current state.
See Webhooks for the events and their payloads.