# Setting up webhooks

The [Webhook API](/nora/api-documentation/webhooks.md) 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:

<table><thead><tr><th width="252.2890625">Event</th><th>Triggered When</th></tr></thead><tbody><tr><td><code>extracted</code></td><td>AI extraction of submission data completes successfully</td></tr><tr><td><code>export</code></td><td>Manual export is initiated for a submission</td></tr><tr><td><code>validation-success</code></td><td>Submission validation passes before export</td></tr><tr><td><code>error-export</code></td><td>Triggered when export fails (both automatic and manual exports)</td></tr><tr><td><code>error-processing</code></td><td>Triggered when submission processing/extraction fails</td></tr><tr><td><code>error-pre-processing</code></td><td>Triggered when email submission pre-processing fails</td></tr></tbody></table>

All webhook events share a common payload structure:

```json
{
  "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.

{% hint style="warning" %}
**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.
{% endhint %}

#### **Signature Headers**

<table><thead><tr><th width="205.23828125">Header</th><th>Description</th><th>Example</th></tr></thead><tbody><tr><td><code>X-Webhook-Signature</code></td><td>HMAC-SHA256 signature (hex-encoded)</td><td><code>a3b2c1d4e5f6...</code></td></tr><tr><td><code>X-Webhook-Timestamp</code></td><td>ISO 8601 timestamp when signature was generated</td><td><code>2023-10-28T12:00:00.000Z</code></td></tr></tbody></table>

### 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.

```javascript
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...
});
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ai.insly.com/nora/configuration-guide/setting-up-webhooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
