From 6e8b4d19cd130f59045a4e27e7c9e52c9cfb388f Mon Sep 17 00:00:00 2001 From: garethx Date: Fri, 25 Sep 2026 14:31:19 +0100 Subject: [PATCH 1/2] formstack: add 1 captured sample A real Formstack Forms WebHook delivery captured on 2026-09-25 through a Hookdeck source, with the x-hookdeck-* headers removed. Formstack sends no event type, so topic_identifier is null and the file is named submission. The README says the name is a label, not a wire value. The HMAC Key and Shared Secret were throwaway values (test1, test), so the signature can be recomputed from the documented wire bytes. Co-Authored-By: Claude Opus 5.5 --- providers/formstack/README.md | 34 ++++++++++++++++++++++ providers/formstack/index.json | 13 +++++++++ providers/formstack/latest/submission.json | 19 ++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 providers/formstack/README.md create mode 100644 providers/formstack/index.json create mode 100644 providers/formstack/latest/submission.json diff --git a/providers/formstack/README.md b/providers/formstack/README.md new file mode 100644 index 0000000..4c7149d --- /dev/null +++ b/providers/formstack/README.md @@ -0,0 +1,34 @@ +# Formstack + +Formstack Forms' WebHook submit action ("Send Data to an External URL"). It fires on +exactly one thing, a form submission, and sends no event type in any header or body field. +That is why `topic_identifier` is `null`. + +`submission` is the name of the file, not a value Formstack sends. Don't match on it, and +don't treat it as Formstack event vocabulary. Route Formstack deliveries on the `FormID` +body field. + +## The sample + +`latest/submission.json` is a real delivery, captured on 2026-09-25 from a test form with +no answer fields. It went through a Hookdeck source, and the `x-hookdeck-*` headers were +removed. Everything else is as received, including the signature. + +- The body arrived as `application/x-www-form-urlencoded`, which is Formstack's default. + The exact wire bytes were `FormID=6606394&UniqueID=1500878955&HandshakeKey=test`. +- `x-fs-signature` is HMAC-SHA256 of those bytes, as lowercase hex prefixed with + `sha256=`. The WebHook's HMAC Key was the throwaway value `test1`, so the signature can + be recomputed: + + ```bash + printf '%s' 'FormID=6606394&UniqueID=1500878955&HandshakeKey=test' | openssl dgst -sha256 -hmac test1 -r + ``` + +- `HandshakeKey` carries the WebHook's Shared Secret, here the throwaway `test`. It is only + present when a Shared Secret is set. + +A mock send re-encodes the body, and the bytes may not match the original. The +signature is only valid against the exact wire bytes above. + +Formstack Documents (formerly WebMerge) is a different product with a different webhook +and is not covered here. diff --git a/providers/formstack/index.json b/providers/formstack/index.json new file mode 100644 index 0000000..86bedb4 --- /dev/null +++ b/providers/formstack/index.json @@ -0,0 +1,13 @@ +{ + "label": "Formstack", + "configs": { + "latest_version": "latest", + "topic_identifier": null + }, + "provenance": { + "latest": { + "sourced_via": "capture", + "sourced_on": "2026-09-25" + } + } +} diff --git a/providers/formstack/latest/submission.json b/providers/formstack/latest/submission.json new file mode 100644 index 0000000..5dd400b --- /dev/null +++ b/providers/formstack/latest/submission.json @@ -0,0 +1,19 @@ +{ + "headers": { + "content-length": "52", + "content-type": "application/x-www-form-urlencoded; charset=utf-8", + "tracestate": "dd=p:60d345bc96c3c585;", + "user-agent": "FormstackWebhook/1.0 (Form 6606394)", + "x-datadog-parent-id": "6976996924012610949", + "x-datadog-sampling-priority": "0", + "x-datadog-tags": "_dd.p.tid=6ab674fb00000000", + "x-datadog-trace-id": "16234198080647658916", + "x-fs-signature": "sha256=30dff7f180b6d69eab397a5d51719474df490b730514c253e8b5832d3b51b970" + }, + "body": { + "FormID": "6606394", + "UniqueID": "1500878955", + "HandshakeKey": "test" + }, + "topic": "submission" +} From 4f7757a97e6023d889e9da0f63505998b38ab21f Mon Sep 17 00:00:00 2001 From: garethx Date: Fri, 25 Sep 2026 14:44:03 +0100 Subject: [PATCH 2/2] formstack: don't assert HandshakeKey is absent without a Shared Secret Co-Authored-By: Claude Opus 5.5 --- providers/formstack/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/providers/formstack/README.md b/providers/formstack/README.md index 4c7149d..30bc7d5 100644 --- a/providers/formstack/README.md +++ b/providers/formstack/README.md @@ -24,8 +24,8 @@ removed. Everything else is as received, including the signature. printf '%s' 'FormID=6606394&UniqueID=1500878955&HandshakeKey=test' | openssl dgst -sha256 -hmac test1 -r ``` -- `HandshakeKey` carries the WebHook's Shared Secret, here the throwaway `test`. It is only - present when a Shared Secret is set. +- `HandshakeKey` carries the WebHook's Shared Secret, here the throwaway `test`. This capture + had a Shared Secret set, so it doesn't show what a WebHook without one sends. A mock send re-encodes the body, and the bytes may not match the original. The signature is only valid against the exact wire bytes above.