Webhook
Webhooks send your form response data to any URL whenever someone submits your form. Use them to connect FormGrid to your own backend, Zapier, Make, or any other service that accepts webhooks.
Setting up a webhook
- Open your form and go to the Connect tab
- Click Connect next to Webhook
- In the Integration Settings dialog, enter a Name for the integration
- Enter your Webhook URL — the endpoint that will receive the data
- (Optional) Click Add signing secret to generate a signing secret for security
- Click Create
Signing secret
The signing secret lets your server verify that incoming webhook requests actually came from FormGrid. When set, FormGrid includes a signature header with each request that your server can validate. This prevents anyone from spoofing submissions to your endpoint.
Event log
Every webhook call is logged. Check the event log to see:
- Whether requests were delivered successfully
- Error messages if delivery failed
- Timestamps for each event
This is your go-to tool for debugging webhook issues.
Use cases
- Custom backends — send form data directly to your app’s API
- Automation platforms — connect to Zapier, Make, n8n, or similar tools
- Slack/Discord notifications — trigger a message when someone submits
- CRM updates — push new leads or contacts into your CRM
Tips
- Test your webhook URL with a test submission before going live
- Check the event log if responses aren’t arriving at your endpoint
- Use the signing secret in production for security
Developer reference
Request format
FormGrid sends a POST request to your webhook URL with a JSON body. Every request includes these headers:
| Header | Value |
|---|---|
Content-Type | application/json |
User-Agent | FormGrid-Webhook |
X-FormGrid-Signature | sha256=<hex> (only if a signing secret is configured) |
Requests time out after 10 seconds. Your endpoint should respond promptly — do any heavy processing asynchronously after returning a 2xx response.
Verifying signatures
If you’ve configured a signing secret, every request includes an X-FormGrid-Signature header. The signature is an HMAC-SHA256 hex digest of the raw request body, prefixed with sha256=.
To verify:
- Read the raw request body (before any JSON parsing)
- Compute
HMAC-SHA256of the body using your signing secret as the key - Compare the resulting hex digest against the value in the header (after stripping the
sha256=prefix)
Example in Node.js:
import crypto from "crypto"
function verifySignature(body, secret, signatureHeader) {
const expected = crypto.createHmac("sha256", secret).update(body).digest("hex")
return signatureHeader === `sha256=${expected}`
}
Event payload
Every webhook delivery sends a single form_response event:
{
"event_id": "Kj7mNpQr2sVwXy4z",
"event_type": "form_response",
"created_at": "2025-01-15T09:30:00.000Z",
"data": {
"form": {
"id": "bR3kWn8fTx1qLm5v",
"created_at": "2024-12-01T12:00:00.000Z",
"title": "Customer Feedback",
"version": 3
},
"form_response": {
"id": "hY6pCd9jNt2wAe4s",
"created_at": "2025-01-15T09:29:58.000Z",
"fields": []
}
}
}
| Property | Type | Description |
|---|---|---|
event_id | string | Unique ID for this webhook delivery |
event_type | string | Always form_response |
created_at | string | ISO 8601 timestamp of the event |
data.form.id | string | Form ID |
data.form.created_at | string | When the form was created |
data.form.title | string | Form title |
data.form.version | number | Current form version |
data.form_response.id | string | Response ID |
data.form_response.created_at | string | When the response was submitted |
data.form_response.fields | array | Array of field objects (see below) |
Field types
Each entry in fields has a common shape and a type-specific value. Fields that the respondent left blank have a value of null.
Short text
{
"id": "a3Bk",
"type": "text_short",
"label": "Your name",
"config": { "required": true, "max_chars": 100 },
"value": "Jane Doe"
}
Long text
{
"id": "Xm9p",
"type": "text_long",
"label": "Comments",
"config": { "required": false, "max_chars": 2000 },
"value": "Great product, very intuitive."
}
{
"id": "Lw2n",
"type": "email",
"label": "Email address",
"config": { "required": true },
"value": "jane@example.com"
}
Multiple choice
Options are denormalized into the payload so you don’t need to look them up. When max_selections is 1, it’s a single-select question — the value array will contain at most one entry. If “other” is enabled and the respondent chose it, the entry will have id: "other" with their freeform text.
{
"id": "Qf7j",
"type": "multiple_choice",
"label": "Favorite color",
"config": {
"required": true,
"max_selections": 1,
"allow_other": true,
"randomize": false
},
"options": [
{ "id": "Rv3m", "text": "Red" },
{ "id": "Jb8w", "text": "Blue" }
],
"value": [{ "id": "Rv3m", "text": "Red" }]
}
An “other” response looks like:
"value": [{ "id": "other", "text": "Purple" }]
Ranking
Similar to multiple choice, but the order of items in value represents the respondent’s ranking from first to last.
{
"id": "Ht4v",
"type": "ranking",
"label": "Rank these features",
"config": { "required": true, "randomize": true },
"options": [
{ "id": "Wn5a", "text": "Speed" },
{ "id": "Ep2k", "text": "Design" },
{ "id": "Fc2x", "text": "Price" }
],
"value": [
{ "id": "Fc2x", "text": "Price" },
{ "id": "Wn5a", "text": "Speed" },
{ "id": "Ep2k", "text": "Design" }
]
}
Rating
The value is a number from 1 to max_score.
{
"id": "Ys8c",
"type": "rating",
"label": "How would you rate us?",
"config": { "required": true, "max_score": 5 },
"value": 4
}
Linear scale
The value is a number between start and end (inclusive).
{
"id": "Dg5r",
"type": "linear_scale",
"label": "How likely are you to recommend us?",
"config": {
"required": true,
"start": 0,
"end": 10,
"start_label": "Not likely",
"end_label": "Very likely"
},
"value": 8
}
File upload
Each file includes a signed URL that is valid for a limited time.
{
"id": "Nk1e",
"type": "file_upload",
"label": "Upload your resume",
"config": {
"required": false,
"allowed_types": ["pdf", "docx"],
"max_files": 3,
"max_size_bytes": 10485760
},
"value": [
{
"id": "mT5xKw3pRn7vQj9s",
"name": "resume.pdf",
"mime_type": "application/pdf",
"size": 204800,
"url": "https://files.formgrid.com/..."
}
]
}
Hidden field
Hidden fields are never shown to the respondent. They’re typically populated via URL parameters.
{
"id": "Pz6u",
"type": "hidden",
"label": "utm_source",
"config": { "required": false },
"value": "newsletter"
}
Error handling
Your endpoint should return a 2xx status code to indicate success. Any other status code is treated as a failure, and the delivery will be logged as an error in the event log. Network errors (DNS failures, timeouts, connection refused) are also logged.
Failed deliveries are automatically retried up to 5 times with increasing delays: 5 minutes, 30 minutes, 1 hour, 6 hours, and 1 day.