Webhooks

Webhooks let you receive real-time HTTP notifications when events happen on your forms. Configure a webhook URL in your form settings and GudForm will POST event data to your endpoint.

Requires Pro plan or higher. Configure webhooks in your form’s builder under Settings → Webhooks.


Setting Up Webhooks

  1. Open your form in the builder and go to the Settings sidebar.
  2. Scroll to the Webhooks section.
  3. Enter your endpoint URL (must be HTTPS in production).
  4. Optionally add a Webhook Secret to verify signatures.
  5. Save — GudForm will start posting events immediately.

Events

form.response.completed

Fired when a form response is fully submitted (and payment confirmed, if applicable). This is the primary event for processing form data.

PayloadJSON
{
  "event": "form.response.completed",
  "formId": "clx1234abcd",
  "formTitle": "Customer Feedback",
  "collectionId": "col_abc123",
  "collectionName": "Surveys",
  "responseId": "resp_abc123",
  "answers": [
    {
      "question": "What is your name?",
      "answer": "Jane Smith"
    },
    {
      "question": "Rate your experience",
      "answer": "5"
    },
    {
      "question": "Any additional feedback?",
      "answer": "Great product, love the UI!"
    }
  ],
  "timestamp": "2025-01-20T14:32:15.000Z"
}

Planned Events

The following events are planned for future releases:

EventDescription
form.response.startedRespondent began filling out a form
form.response.payment.completedPayment for a response was confirmed
form.response.payment.failedPayment for a response failed or expired
form.publishedForm was published
form.closedForm was closed
form.movedForm was moved to a different collection
collection.createdA new collection was created
collection.deletedA collection was deleted

Request Headers

Every webhook request includes these headers:

HeaderDescription
Content-TypeAlways application/json
User-AgentGudForm-Webhook/1.0
X-GudForm-SignatureHMAC-SHA256 signature (when secret is configured)

Verifying Webhook Signatures

If you configure a webhook secret, every request will include an X-GudForm-Signature header containing an HMAC-SHA256 digest of the request body. Always verify this signature to ensure the request is from GudForm.

Signature Format

Header value
X-GudForm-Signature: sha256=a1b2c3d4e5f6...

Verification Example (Node.js)

verify-webhook.tsTypeScript
import crypto from "crypto";

function verifySignature(
  body: string,
  signature: string,
  secret: string,
): boolean {
  const expectedSig = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");

  const expected = `sha256=${expectedSig}`;

  // Use timing-safe comparison to prevent timing attacks
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected),
  );
}

// In your webhook handler:
app.post("/webhooks/gudform", (req, res) => {
  const signature = req.headers["x-gudform-signature"];
  const body = JSON.stringify(req.body);

  if (!verifySignature(body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  // Process the webhook...
  const { event, formId, answers } = req.body;
  console.log(`Received ${event} for form ${formId}`);

  res.status(200).json({ received: true });
});

Verification Example (Python)

verify_webhook.pyPython
import hmac
import hashlib

def verify_signature(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, f"sha256={expected}")

# In your Flask handler:
@app.route("/webhooks/gudform", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-GudForm-Signature", "")
    if not verify_signature(request.data, signature, WEBHOOK_SECRET):
        return {"error": "Invalid signature"}, 401

    data = request.json
    print(f"Received {data['event']} for form {data['formId']}")
    return {"received": True}

Retry Policy

If your endpoint returns a non-2xx status code or times out, GudForm will retry the delivery:

AttemptDelayTimeout
1st (initial)Immediate10 seconds
2nd retry1 second10 seconds
3rd retry4 seconds10 seconds

After 3 failed attempts, the delivery is marked as failed. We recommend implementing idempotent handlers using the responseId as a deduplication key.


Best Practices

  1. Return 200 quickly. Process webhook data asynchronously (e.g. add to a queue) and return 200 immediately.
  2. Verify signatures. Always validate the X-GudForm-Signature header when a secret is configured.
  3. Handle duplicates. Use responseId as an idempotency key — the same event may be delivered more than once.
  4. Use HTTPS. Webhook URLs should use HTTPS in production to protect data in transit.
  5. Monitor failures. Log failed deliveries and set up alerts for repeated failures.