Home / Features / Webhooks
Native Webhooks

Webhooks that actually deliver

Sign-verified JSON payloads, queue-backed delivery, exponential retries, and a per-form delivery log. Built for developers — no Zapier middle layer eating your data.

Free on every plan · HMAC-signed · Queued retries
POST /webhook
{
  "event": "submission.created",
  "delivery_id": "dlv_8aE2k",
  "timestamp": 1730723400,
  "form_id": "f_142",
  "submission_id": "s_9k3X",
  "fields": {
    "name": "Sarah Chen",
    "email": "[email protected]",
    "budget": 25000
  }
}

What triggers a webhook

You configure once per form. We dispatch on every relevant event.

submission.created

New submission

Fires the moment a form is submitted, with the full response payload, fields, and metadata.

payment.succeeded

Payment success

When a Stripe payment attached to a submission succeeds, the webhook includes payment status, amount, and currency.

draft.resumed

Draft resumed

When a respondent returns to a saved draft (save-and-resume forms), you can be notified for funnel analytics.

Predictable, versioned, well-shaped JSON

No surprises. Same envelope on every event. Field types preserved as-typed.

{
  "event":         "submission.created",
  "delivery_id":   "dlv_8aE2kF1Ld9zQ",
  "timestamp":     1730723400,
  "form_id":       "f_142",
  "submission_id": "s_9k3XnPq72T",
  "fields": {
    "name":    "Sarah Chen",
    "email":   "[email protected]",
    "company": "Acme Corp",
    "budget":  25000,
    "message": "Looking for a form solution..."
  },
  "payment": {
    "status":   "succeeded",
    "amount":   2500,
    "currency": "usd"
  },
  "meta": {
    "ip":         "203.0.113.42",
    "user_agent": "Mozilla/5.0...",
    "locale":     "en"
  }
}

Sign verified, never spoofed

Every payload is signed with an HMAC-SHA256 of your form's webhook secret. Verify on your end before trusting the data.

HMAC-SHA256 signing

X-FormBuilder-Signature header on every request. Compute the HMAC of the raw body using your secret, compare in constant time.

Per-form secrets

Each form gets its own secret. Rotate anytime from form settings — old payloads stay verifiable until you regenerate.

TLS only

Only HTTPS endpoints are accepted. We refuse to dispatch to plaintext URLs to protect submission data in transit.

Replay protection

Each payload includes a timestamp and unique delivery ID. Reject deliveries older than 5 minutes to prevent replay attacks.

Verify in 3 lines

Pick your stack. The pattern is the same: HMAC the raw body with your secret, compare to the signature header.

const crypto = require('crypto');

function verify(rawBody, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}
function verify($rawBody, $signature, $secret) {
    $expected = hash_hmac('sha256', $rawBody, $secret);
    return hash_equals($expected, $signature);
}
import hmac, hashlib

def verify(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Built for the real world

Networks fail, endpoints throttle, deploys happen. Webhooks queue, retry, and surface delivery state.

Queued delivery

Submissions return immediately to your respondent. Webhook delivery happens in a background queue — never blocks the form.

Exponential backoff

Failed deliveries retry with exponential backoff. We don't hammer your endpoint when it's already struggling.

Multiple retry attempts

Each delivery is retried multiple times across hours. You have time to recover from outages without losing data.

Delivery log

Every attempt is recorded — request, response code, body excerpt, timestamp. Inspect failures in the form settings.

Idempotency keys

Each delivery includes a unique ID. Use it as an idempotency key on your end to safely de-dupe retries.

Audit logged

Webhook URL changes, secret rotations, and toggle on/off events are all recorded in your workspace audit log.

What people build with webhooks

USE CASE 01

Custom CRM sync

Push leads into your in-house CRM with the exact field mapping your system expects.

USE CASE 02

Slack / Discord alerts

Beyond the native Slack integration, build custom alert flows with rich payloads.

USE CASE 03

Backend processing

Trigger background jobs, send transactional email, run validation, sync to your data warehouse.

USE CASE 04

Zapier / Make alternative

Skip the middleware fee. A single webhook can fan out to any number of internal services.

Setup in 4 steps

Configure once, deliver forever.

Open form settings

In the dashboard, open your form and go to Integrations → Webhooks.

Add your endpoint URL

Paste the HTTPS URL that should receive submissions. We validate that the endpoint responds before saving.

Copy the signing secret

Each form gets a unique secret. Store it server-side and use it to verify incoming payloads.

Submit a test

Send a test submission and inspect the delivery log. Verify your endpoint returned 2xx and your signature check passed.

Frequently asked questions

Are webhooks free?
Yes. Webhooks are included on every plan, including the free tier. No per-call charges.
How fast is delivery?
Delivery is queue-backed and typically completes within seconds of submission. We don't block the form response, so respondents see instant confirmation.
What is the retry policy?
Failed deliveries (5xx, timeouts, connection errors) retry with exponential backoff across multiple attempts over hours. After max retries, the delivery is marked failed and logged.
Can I have multiple webhooks per form?
Yes. Add as many webhook endpoints as you need on a single form. Each fires independently with its own secret and retry state.
Do you support an IP allow-list?
Webhook deliveries originate from a stable set of IP addresses. Contact support for the current list if your firewall requires explicit allow-listing.
How do I rotate the signing secret?
In form settings → webhook → Regenerate Secret. The new secret takes effect on the next delivery. Update your endpoint to verify with the new secret before regenerating.

Ready to wire it up?

Native webhooks. Free on every plan.

Get Started — It's Free