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:

Event
Triggered When

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.

Signature Headers

Header
Description
Example

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:

  1. Respond quickly - Return a 2xx status code within 5 seconds

  2. Be publicly accessible - The webhook service must be able to reach your endpoint

  3. 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?