A webhook sends a submission's data to a URL you choose, as soon as it happens. If you have a system that can accept an incoming request (your own application, a service like Make or n8n, a Slack incoming webhook), a webhook connects Flow Forms to it directly, with no third party in between.
In Flow Forms, a webhook is a trigger you add to a form's workflow, the same way you would add a notification or an approval step. That means you decide exactly when it fires.
Webhooks or Zapier?
Both send submission data out of Flow Forms. Pick based on where it is going:
| Webhook | Zapier | |
|---|---|---|
| Best for | A system you or your developer control | Off-the-shelf apps: Sheets, Slack, your CRM |
| Setup | Entirely inside Flow Forms | Built in Zapier, then finished in Flow Forms |
| Needs a developer? | Usually, to receive the request | No |
| Control over the request | Full: body, headers, format | None; Zapier decides |
If you are trying to get submissions into a well-known app and nobody on your team writes code, use Zapier. If you have an endpoint waiting for the data, use a webhook.
Adding a Webhook
- Go to Admin → Forms and open the form.
- Scroll to Flow Steps and click Add Step → Trigger.
- Choose Post to Webhook.
- Fill in the settings below, then click Save.
Trigger Name
A label for your own benefit. It is what you will see on the card in the workflow. Something like "Send to billing system" beats "Webhook 1".
When Should This Trigger Run
| Option | When the webhook fires |
|---|---|
| Upon form submission | As soon as someone submits the form |
| On approval of a specific flow | When a particular step is approved (you choose which) |
| Upon final approval | When the submission finishes the whole workflow |
| Upon deny | When the submission is denied |
| Upon Demand | Only when a conditional step or a scheduled automation calls it |
Upon Demand means the webhook does nothing on its own. Choose it when you want a conditional step or a scheduled automation to decide when it fires.
An administrator can also run any webhook on a form by hand from a submission's Run automation menu — including ones that normally fire on submit or approval. That is how you resend a delivery that failed.
URL
The address the data is posted to, as a full URL. Use https:// - see Security for why.
Content Type
How the data is packaged in the request:
- JSON - sent as a JSON object. This is what most modern applications expect.
- Form-encoded - sent the way an HTML form posts. Some older endpoints require this.
If you are not sure, ask whoever owns the receiving end. JSON is the safer default.
Authorization Token (optional)
If the receiving system needs a token to accept the request, paste it here and Flow Forms sends it in the Authorization header. You can include the Bearer prefix or leave it off; Flow Forms adds it if it is missing.
Headers (optional)
Extra headers, added as key-and-value pairs, for endpoints that need something beyond a bearer token: an API version, a shared secret in a custom header, a routing key.
Header values can use the same placeholders as body fields, so you can pass the submission ID in a header if that is what the receiving system expects.
Body Fields (optional)
Leave this empty and Flow Forms sends every field on the submission, using each field's name as the key and its answer as the value:
{
"Employee Name": "Jane Doe",
"Department": "Facilities",
"Start Date": "2026-03-15"
}
Add body fields when the receiving system expects a specific shape. Each row is a key and a value, and the value can be fixed text, a placeholder, or a mix of both. Adding even one row replaces the default body entirely, so only the rows you define are sent.
For example, these three rows:
| Key | Value |
|---|---|
text |
New request from {{form}} |
title |
{{field:Employee Name}} |
link |
{{submission_url}} |
produce:
{
"text": "New request from Onboarding Request",
"title": "Jane Doe",
"link": "https://yoursite.flowforms.app/submission/sub_abc123"
}
Placeholders
| Placeholder | Becomes |
|---|---|
{{sqid}} |
The submission's ID, e.g. sub_abc123 |
{{field:Name}} |
The answer to the field called Name |
{{submission_url}} |
A link to the submission in Flow Forms |
{{form}} |
The name of the form |
{{field:Name}} has to match the field's name exactly, including capitalisation and spaces. If it does not match anything, it resolves to an empty value rather than an error, so if a value arrives blank, check the field name first.
Security
Flow Forms does not sign its webhook requests. There is no signature header to verify, so if your endpoint is reachable from the internet, anyone who learns the URL can post to it.
To confirm a request genuinely came from Flow Forms:
- Set an Authorization Token (a long random string) and have your endpoint reject anything that does not match. This is the simplest reliable check.
- Or send a shared secret in a custom header and check that instead.
- Always use an
https://URL so the token is not sent in the clear. - Treat the data as untrusted input at your end, exactly as you would a public form post.
Anyone who can edit the form's workflow can see and change the token, so use a value dedicated to this purpose rather than reusing a password or a production API key with broad access.
When Delivery Fails
Your endpoint has 30 seconds to respond. If it needs longer to do the actual work, accept the request straight away and process it in the background.
- A failed delivery is not retried automatically. If your endpoint returns an error or cannot be reached, that attempt does not arrive. The submission itself is unaffected - but any triggers set to run after this webhook on the same event are skipped, so put a webhook last among its event's triggers unless the things after it should only happen when delivery succeeds.
- You can resend it from the submission. Open the submission, use Run automation in the header, and pick the webhook. Administrators and form admins (anyone with permission to manage triggers) can do this whether the webhook is Upon Demand or tied to submit, approval, or deny.
- The exception is rate limiting. If your endpoint replies that it is too busy, Flow Forms waits and tries that delivery again.
- Every failure is recorded under Admin → Error Log, with the form and submission attached and the reason: the HTTP status, or a note that the endpoint could not be reached. Repeated failures of the same webhook are grouped into a single entry with a count, so a broken endpoint shows up as one growing entry rather than hundreds of separate ones. The log links to the submission so you can resend from there.
- A webhook with no URL is skipped and logged, rather than failing quietly.
After you first turn a webhook on, check the Error Log and retry any missed deliveries from the submission. Check it occasionally afterwards as well.
Testing a Webhook
Before pointing a webhook at a live system, send it somewhere you can inspect:
- Open webhook.site and copy the unique URL it gives you.
- Add a Post to Webhook trigger with that URL, set to Upon form submission.
- Submit a test entry on the form.
- Watch the request arrive on webhook.site and check the body and headers are what your real endpoint expects.
- Change the URL to the real endpoint and save.
Troubleshooting
Nothing arrives at my endpoint
Check the trigger is not sitting under Upon Demand in Flow Steps. That setting waits to be called and fires on nothing by itself. Then check Admin → Error Log for the form; a failed delivery is always recorded there. If the delivery failed, open the submission and use Run automation to send it again.
The Error Log shows a 401 or 403
Your endpoint is rejecting the request. Confirm the Authorization Token matches what the receiving system expects, and that any required custom headers are present.
The Error Log says the endpoint could not be reached
Usually a wrong URL, an endpoint that is not publicly reachable, or an expired certificate. Confirm you can reach the same URL from outside your network.
A field in my body arrives empty
The {{field:Name}} placeholder did not match a field. Open the form builder and compare the name character for character; the placeholder is exact.
One of my custom headers never arrives
Header names may only contain letters, numbers and a few symbols, but no spaces. A name with a space in it is dropped rather than sent. Line breaks are also stripped from header values.
My endpoint is slow and deliveries keep failing
Respond within 30 seconds. Acknowledge the request immediately and do the slow work afterwards, rather than holding the connection open.
Getting Help
If you are still stuck, contact support with the name of the form and the entries from Admin → Error Log, which carry the exact status and reason for each failure.