Setting up webhooks
The Webhook API allows you to receive real-time notifications when specific events occur in your FormFlow submissions. By subscribing to webhook events, your application can automatically respond to submission processing milestones without polling.
Available Event Types
The webhook system supports six event types:
extracted
AI extraction of submission data completes successfully
export
Manual export is initiated for a submission
validation-success
Submission validation passes before export
error-export
Triggered when export fails (both automatic and manual exports)
error-processing
Triggered when submission processing/extraction fails
error-pre-processing
Triggered when email submission pre-processing fails
All webhook events share a common payload structure:
{
"eventId": "123e4567-e89b-12d3-a456-426614174000",
"eventType": "extracted",
"timestamp": "2023-10-28T12:00:00.000Z",
"submissionId": "123e4567-e89b-12d3-a456-426614174001",
"templateId": 1
}Webhook Security
When creating a subscription for an event, you will receive a secret token which you can use to verify whether the webhook is sent by us and is unchanged.
When we send out webhook events a signature is included in the header. This is created by hashing the payload secret before transport. You can use the token to hash the payload on your end using HMAC-SHA256 (hex-encoding) and compare if the signatures match to confirm that the payload is valid and unchanged.
Important: You must use the exact raw request body string to compute the hash, not a re-serialized version of the parsed JSON. Different JSON serialization can produce different strings even for the same data.
Signature Headers
X-Webhook-Signature
HMAC-SHA256 signature (hex-encoded)
a3b2c1d4e5f6...
X-Webhook-Timestamp
ISO 8601 timestamp when signature was generated
2023-10-28T12:00:00.000Z
Receiving Webhooks
Endpoint Requirements
Your webhook endpoint should:
Respond quickly - Return a 2xx status code within 5 seconds
Be publicly accessible - The webhook service must be able to reach your endpoint
Handle idempotency - The same event may be delivered multiple timesx
Below is an example on how verification works.
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Here we have a separate endpoint for an event, it is also possible to
// create logic that chooses the correct secret based on the "type" field
app.post('/webhooks/formflow/extracted', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const secret = process.env.WEBHOOK_SECRET_EXTRACTED;
if (!signature || !verifyWebhookSignature(req.body, signature, secret)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process verified webhook...
});Last updated
Was this helpful?