{"templateId":"markdown","sharedDataIds":{},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Signed webhooks","siteUrl":"https://developers.signable.app","description":"Integrate Signable’s eSignature API to send, sign, and manage documents with envelopes and webhooks.","image":"/assets/hero-placeholder-small.5b052639e5d5a3b6265375db1ac2835173dda2c0ed22f8c167f6284080cc84ce.db81178d.png","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"signed-webhooks","__idx":0},"children":["Signed webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Signable signs each webhook request so you can verify that the request came from Signable and that the payload was not modified."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Early access"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This feature is in early access and is currently available to selected customers."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"how-are-webhooks-signed","__idx":1},"children":["How are webhooks signed?"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you create a webhook using ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/openapi/webhooks/createwebhook"},"children":["POST /webhooks"]},", we generate a secret for that webhook."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We return this value in the response as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook_secret"]},"."]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["This field is only returned when the webhook is created."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You must store it securely."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You need it later to verify webhook signatures."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If you lose it, you must create a new webhook."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each webhook request is signed using ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["HMAC-SHA256"]},", then encoded using Base64 before it is sent."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"example-request","__idx":2},"children":["Example Request"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"http","header":{"controls":{"copy":{}}},"source":"POST /your-webhook-endpoint HTTP/1.1\nHost: example.com\nContent-Type: application/json\nSignature: bXlfc2lnbmF0dXJl\nX-Signable-Webhook: 1710000000\n","lang":"http"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"verify-the-signature","__idx":3},"children":["Verify the signature"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To verify a webhook request:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Read the raw request body exactly as received."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Generate the signature using your webhook secret: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["HMAC_SHA256(your_secret, payload)"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Base64 encode the result."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Compare your generated value with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Signature"]}," header."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Reject the request if the values do not match."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"signature-format","__idx":4},"children":["Signature format"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The signature is generated using:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["algorithm: HMAC-SHA256"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["encoding: base64"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["signed content:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"<raw_body>\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The timestamp is included in the payload and in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Signable-Webhook"]}," header."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"prevent-replay-attacks","__idx":5},"children":["Prevent replay attacks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each request includes a timestamp in both the payload and the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Signable-Webhook"]}," header."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To reduce replay risk:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Check that the timestamp is recent (for example, within 5 minutes)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Reject requests with timestamps outside your allowed window."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This limits the usefulness of captured requests."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"webhook-secret","__idx":6},"children":["Webhook secret"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each webhook subscription has its own secret."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Store the secret securely. Anyone with access to the secret could generate valid signatures for that webhook subscription."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"related-documentation","__idx":7},"children":["Related documentation"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/openapi/webhooks/createwebhook"},"children":["Create a webhook"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/openapi/webhook-events"},"children":["Webhook events"]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["⬅️ Back to ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/early-access/ea-webook-improvements"},"children":["Webhook improvements"]}]}]},"headings":[{"value":"Signed webhooks","id":"signed-webhooks","depth":1},{"value":"How are webhooks signed?","id":"how-are-webhooks-signed","depth":2},{"value":"Example Request","id":"example-request","depth":3},{"value":"Verify the signature","id":"verify-the-signature","depth":2},{"value":"Signature format","id":"signature-format","depth":3},{"value":"Prevent replay attacks","id":"prevent-replay-attacks","depth":3},{"value":"Webhook secret","id":"webhook-secret","depth":3},{"value":"Related documentation","id":"related-documentation","depth":3}],"frontmatter":{"title":"Signed webhooks","description":"Verify webhook events from Signable using HMAC signatures","category":"Reference","keywords":["webhooks","security","hmac","signatures"],"seo":{"title":"Signed webhooks"}},"lastModified":"2026-03-19T16:54:44.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/early-access/ea-signed-webhooks","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}