Signed JSON to your own endpoint
When the destination is your own backend, a serverless function or a tool with no native integration, a webhook is the integration. Each submission arrives as one JSON event, keyed by field key, signed so you can prove it came from Formstep, and retried until your endpoint says yes.
Updated
How it works
- Open the form’s Integrations, pick Webhook, and type the URL. Leave the mapping empty to send every field, or add rows to send only some, under the JSON keys you choose. See an example request shows the body change as you edit.
- Copy the signing secret, which starts with
whsec_and is shown once. Add up to five custom headers, such as an authorization token. - Click Turn on Webhook, then Send a test event: a signed POST with sample answers and your headers, logged in the event log like a real delivery.
What arrives
One envelope for everything Formstep sends: id, type, createdAt, apiVersion, test and data. Under data: the form, the submission with its id, respondent email, time, language and PDF link, an answers object for code, keyed by field key, and a display object with the same keys as readable text. A choice answer is its option key in answers and its label in display; a file is a list with download URLs in one and file names in the other. A submission that answered a request adds a request block with your external id and metadata.
X-Formstep-Signature: t=TIMESTAMP,sha256=HEX
A form can have several webhooks; each fires for every submission and every later edit, told apart by type. Turn on Send the field list with every event if your receiver cannot call fields.list itself.
Retries
Any 2xx is a success. Formstep tries up to five times: at once, then after at least 1, 2, 4 and 8 minutes, so the last attempt comes about two hours after the first. 429, 5xx, timeouts and connection failures are retried, and a longer Retry-After is honoured. 401, 403 and 404 stop the integration at once and email the person who set it up. Deliveries that used all five attempts show in a banner on the integration; Retry all sends them again and turns a paused integration back on.
For local development, expose your dev server with ngrok or a Cloudflare tunnel and point the webhook at it. For unfinished public-link responses, create a submission_abandoned subscription with the REST API; the webhook you add here never fires for them.
A workflow tool with a webhook trigger, such as Make or Pipedream, can receive these too. If the receiver is a workflow waiting on one specific person, you want a request’s callback rather than a webhook: it fires once, for that request, with its outcome.
