Providers / April 25, 2026

How to receive Typeform submissions as webhooks (form responses)

Typeform webhook flow showing form_response delivery to a triage agent

Typeform fires a webhook for every form submission, delivering the response payload to a URL of your choice. They're how forms become triggers — for lead routing, support intake, qualification flows, survey analysis, and any AI agent that needs to react to "a user filled out a form."

This guide covers what Typeform sends, how to verify it, and what production reliability requires.

What Typeform sends

Typeform webhooks are JSON POST requests. A form_response event:

{
  "event_id": "01HM3K8N2P9X4ABCDEFGHIJKLM",
  "event_type": "form_response",
  "form_response": {
    "form_id": "abc12345",
    "token": "01HM3K8N2P9X4ABCDEFGHIJKLM",
    "submitted_at": "2026-04-25T14:30:00Z",
    "definition": {
      "id": "abc12345",
      "title": "Demo Request",
      "fields": [
        { "id": "fld_001", "title": "What's your name?", "type": "short_text", "ref": "name" },
        { "id": "fld_002", "title": "Company size?", "type": "multiple_choice", "ref": "size" }
      ]
    },
    "answers": [
      { "field": { "id": "fld_001", "type": "short_text", "ref": "name" }, "type": "text", "text": "Octavia Chen" },
      { "field": { "id": "fld_002", "type": "multiple_choice", "ref": "size" }, "type": "choice", "choice": { "label": "50-200" } }
    ]
  }
}

Headers:

Typeform-Signature: sha256=<base64 signature>
Content-Type: application/json

Typeform's main event is form_response — most integrations only need to handle one event type. Each response includes the full form definition (so you can resolve answers without keeping a separate copy of the schema) and the user's answers keyed by field ID and ref.

Typeform's signature scheme

Typeform signs each webhook with HMAC-SHA256 of the raw body, using your webhook secret, base64-encoded:

signature = "sha256=" + base64(hmac_sha256(secret, raw_body))

Verification:

  1. Read the raw body before any JSON parsing
  2. Compute HMAC-SHA256(secret, raw_body) and base64-encode
  3. Prefix with sha256= and compare constant-time with the Typeform-Signature header

There's no timestamp in the signature header, so use event_id for idempotency and the submitted_at field in the payload to detect stale events.

Typeform's retry policy

Typeform retries failed webhooks (non-2xx, or no response within ~5 seconds) on a backoff schedule. The retry window is finite — past it, the response is marked failed in the form's webhook log and you have to either re-trigger manually or backfill from the Responses API.

This means:

  • Acknowledge fast — return 2xx in under one second
  • Be idempotent on event_id — duplicates will arrive during retries
  • Have a recovery story — backfill via Typeform's Responses API for events past the retry window

DIY: minimal Typeform webhook handler in Node

import { createHmac, timingSafeEqual } from 'crypto'

const secret = process.env.TYPEFORM_WEBHOOK_SECRET!

export async function POST(req: Request) {
  const sig = req.headers.get('typeform-signature')!
  const body = await req.text()

  const expected =
    'sha256=' + createHmac('sha256', secret).update(body).digest('base64')
  if (!timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
    return new Response('Bad signature', { status: 400 })
  }

  // Idempotency on event_id
  // ...

  const payload = JSON.parse(body)
  queueWork(payload.form_response)

  return new Response('ok', { status: 200 })
}

Production needs more:

  • A persistence layer for event_id idempotency
  • A queue for async work (you'll likely call enrichment APIs or LLMs in response to the form)
  • A backfill path via Typeform's Responses API
  • Routing — multiple forms POSTing to the same handler need to dispatch on form_id
  • Multi-form support — each form has its own webhook secret

Hooksbase: receive Typeform webhooks without rebuilding the rest

Typeform isn't one of Hooksbase's five pre-verified provider packs (Stripe, GitHub, Clerk, Slack, Resend), and Hooksbase does not forward Typeform's original signature header to your destination. If you need Typeform HMAC verification, verify it in a small pre-ingest forwarder, then post the verified raw body to Hooksbase with the bearer ingest secret.

Setup:

  1. Create a webhook in Hooksbase with your application URL as the destination
  2. Add your verification forwarder URL to your Typeform webhook configuration
  3. In the forwarder, verify the Typeform signature, then POST the same raw body to the Hooksbase ingest URL with Authorization: Bearer <ingest secret>

You get:

  • Acknowledge in milliseconds to Typeform regardless of how slow your downstream is — useful when the form triggers an LLM-backed enrichment
  • Idempotency for your destination — every dispatch includes a unique webhook-id header (Standard Webhooks-compatible) you can dedupe on across retries
  • Retries with exponential backoff to your endpoint after Hooksbase accepts the event
  • Routing rules by form_id or any answer field — send qualified leads to your sales agent, support requests to triage, surveys to analytics
  • Payload transforms — flatten Typeform's verbose structure (definition + answers arrays) into a flat object keyed by field ref
  • Deterministic replay — re-run a failed delivery with the same payload bytes while the payload is retained
  • Delivery history and DLQ

Common Typeform webhook use cases for AI agents

  • Lead qualification agentform_response from a demo request triggers an agent that enriches the lead, scores it, and routes to the right SDR
  • Support intake agentform_response from a contact form triggers an agent that classifies the request, attaches related docs, and either auto-resolves or escalates
  • Survey analysis agentform_response from an NPS or feedback form triggers an agent that extracts themes and routes negative feedback to CS
  • Application screening agentform_response from a job application triggers an agent that pre-screens against role criteria and surfaces strong candidates
  • Onboarding form agentform_response from an onboarding flow triggers an agent that provisions resources based on the answers

Where to go next

Start free at app.hooksbase.com.

Related guides