Skip to main content
Webhooks let you react to form responses in real time. Add a webhook to a form and Formo will POST every new submission to the URL you choose, the moment it arrives.
  • Integrations. Point it at Zapier, Make, n8n, or your own backend to push responses into a CRM, spreadsheet, or internal tool.
  • Slack out of the box. Paste a Slack incoming-webhook URL and responses show up as a formatted message in your channel, with no extra setup.
  • Signed and secure. Optionally sign every payload so your server can verify it genuinely came from Formo.

Setup

1

Open form settings

Open your form in the builder and go to Settings → Webhooks.
2

Add the endpoint

Click Add webhook and paste your endpoint URL. Give it an optional name so it’s easy to recognize later.
3

Keep the signing secret

A signing secret is generated for you. Copy it now; it’s stored encrypted and never shown again. Use it to verify incoming requests (see Verifying signatures).
4

Test it

Click Test to send a sample response to your endpoint and confirm it’s wired up, then toggle the webhook on.
A form can have up to 10 webhooks, and you can disable any of them with its toggle.

Slack

To post responses into a Slack channel, create a Slack incoming webhook and paste its URL (it looks like https://hooks.slack.com/services/...). Formo detects Slack automatically and sends a formatted Block Kit message. No signing secret is needed; keep the URL private, since anyone who has it can post to your channel.

Sample Payload

Non-Slack endpoints receive a JSON envelope:
answers keeps the order and human labels the respondent saw; fields is a flat id to value map that’s easy to bind in automation tools. Field ids are stable across label edits, so key on id, not label.

Headers

Security

When a signing secret is set, Formo signs each request so your server can prove it genuinely came from Formo and wasn’t tampered with. A valid signature can only be produced with the secret (authenticity), it covers the exact body (integrity), and it includes a timestamp your server can use to reject old requests (replay protection). Secrets are encrypted at rest and never returned by the API. Only public http(s) endpoints are allowed; URLs that resolve to private or internal addresses are rejected. The signature is different on every request because it covers both the timestamp and the body, which carries a fresh event id. Don’t compare it to a stored value: recompute it per request from that request’s timestamp, body, and your secret, then compare to the X-Webhook-Signature header.

Verifying signatures

Verify against the raw request body, before parsing JSON. Re-serializing changes whitespace and key order, and the signature won’t match. When testing on webhook.site, the X-Webhook-Signature and X-Webhook-Timestamp headers appear in the request’s Headers section.

Delivery

Delivery happens after the response is saved and never blocks the submitter, so a slow or dead endpoint can’t fail or delay a submission. Failed deliveries are retried a few times on transient errors (network issues, timeouts, 5xx). Deduplicate on data.response_id if a retry could double-process on your side.