πVerify the signature
Prove that a webhook request really came from Dapta Forms: recompute the HMAC-SHA256 signature over the raw body with your signing secret and compare it to the X-Forms-Signature header in constant tim
When you set a Signing secret on a webhook, every request Dapta Forms sends carries an X-Forms-Signature header. Your endpoint can recompute that signature with the same secret and reject anything that does not match. This page shows exactly how the signature is built and gives you copy-paste code for Node.js and Python.
No screenshots here: everything happens on your server.
How the signature is built
Algorithm
HMAC-SHA256
Key
the Signing secret you typed in the webhook card
Message
the raw request body, byte for byte, exactly as received
Header
X-Forms-Signature
Format
sha256= followed by the lowercase hex digest
Example header:
X-Forms-Signature: sha256=b5b23906929f0809297e68964ed4b86c2baec69003c36585c389b65ea9474ea1Two things matter when you verify:
Use the raw body. Compute the HMAC over the bytes you received, before any JSON parsing or re-serialising. Frameworks that parse JSON automatically often change whitespace or key order, and the digest will no longer match. Read the raw body first, verify, then parse.
Compare in constant time. Use your language's timing-safe comparison (
crypto.timingSafeEqualin Node,hmac.compare_digestin Python) instead of==, so an attacker cannot learn the signature one byte at a time.
If the webhook has no signing secret, the header is simply absent and there is nothing to verify. You can add a secret at any time from the form's Connect tab; new deliveries are signed from that moment on.
Node.js (Express)
Python (Flask)
Check it with Send test
You do not need a real respondent to try your code. In the form's Connect tab, click Send test on the webhook card: the test request is signed with the same secret and the same scheme as a real delivery, so a verifier that accepts the test will accept real submissions too. The test body carries "test": true inside data, so you can skip it in your business logic if you want.
π‘ Tip: The other headers help you go further.
x-forms-deliveryis an idempotency key you can store to ignore duplicates, andx-forms-timestamp(Unix seconds) lets you reject requests that are too old for your taste. See Payload & headers reference.
β οΈ Note: A
401or any other non-2xx answer from your endpoint is treated as a failed attempt and is retried with backoff. If you roll a new secret, update it in Dapta Forms and on your server at the same time, otherwise in-flight retries will be rejected until they run out.
What's next
Payload & headers reference: every field and header in the request.
Test & delivery history: see what was sent and what your endpoint answered.
Delivery, retries & history: how retries and failures work.
Last updated