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
- Open your form in the builder and go to the Settings sidebar.
- Scroll to the Webhooks section.
- Enter your endpoint URL (must be HTTPS in production).
- Optionally add a Webhook Secret to verify signatures.
- 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.
{
"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:
| Event | Description |
|---|---|
form.response.started | Respondent began filling out a form |
form.response.payment.completed | Payment for a response was confirmed |
form.response.payment.failed | Payment for a response failed or expired |
form.published | Form was published |
form.closed | Form was closed |
form.moved | Form was moved to a different collection |
collection.created | A new collection was created |
collection.deleted | A collection was deleted |
Request Headers
Every webhook request includes these headers:
| Header | Description |
|---|---|
Content-Type | Always application/json |
User-Agent | GudForm-Webhook/1.0 |
X-GudForm-Signature | HMAC-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
X-GudForm-Signature: sha256=a1b2c3d4e5f6...Verification Example (Node.js)
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)
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:
| Attempt | Delay | Timeout |
|---|---|---|
| 1st (initial) | Immediate | 10 seconds |
| 2nd retry | 1 second | 10 seconds |
| 3rd retry | 4 seconds | 10 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
- Return 200 quickly. Process webhook data asynchronously (e.g. add to a queue) and return 200 immediately.
- Verify signatures. Always validate the
X-GudForm-Signatureheader when a secret is configured. - Handle duplicates. Use
responseIdas an idempotency key — the same event may be delivered more than once. - Use HTTPS. Webhook URLs should use HTTPS in production to protect data in transit.
- Monitor failures. Log failed deliveries and set up alerts for repeated failures.