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

  1. Open your form and go to the Connect tab
  2. Click Connect next to Webhook
  3. In the Integration Settings dialog, enter a Name for the integration
  4. Enter your Webhook URL — the endpoint that will receive the data
  5. (Optional) Click Add signing secret to generate a signing secret for security
  6. 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:

HeaderValue
Content-Typeapplication/json
User-AgentFormGrid-Webhook
X-FormGrid-Signaturesha256=<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:

  1. Read the raw request body (before any JSON parsing)
  2. Compute HMAC-SHA256 of the body using your signing secret as the key
  3. 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": []
    }
  }
}
PropertyTypeDescription
event_idstringUnique ID for this webhook delivery
event_typestringAlways form_response
created_atstringISO 8601 timestamp of the event
data.form.idstringForm ID
data.form.created_atstringWhen the form was created
data.form.titlestringForm title
data.form.versionnumberCurrent form version
data.form_response.idstringResponse ID
data.form_response.created_atstringWhen the response was submitted
data.form_response.fieldsarrayArray 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."
}

Email

{
  "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.