Webhook Documentation
Receive real-time HTTP notifications when events happen in your ParseFlow account. Webhooks eliminate the need for polling and enable event-driven workflows.
Overview
Webhooks send HTTP POST requests to your specified URL when events occur. This is ideal for:
- Triggering downstream workflows when documents are processed
- Receiving batch completion notifications
- Monitoring quota usage in real-time
- Building event-driven integrations with tools like Zapier or n8n
Plan requirement: Webhooks are available on Pro and Enterprise plans. Upgrade your plan to get started.
Setup
Register a webhook endpoint using the API:
curl -X POST https://parseflow.dev/api/webhooks/notifications \
-H "X-API-Key: dm_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.com/webhooks/parseflow",
"events": ["extraction.completed", "extraction.failed"]
}'Response
{
"id": "wh_a1b2c3d4e5f6g7h8",
"url": "https://your-app.com/webhooks/parseflow",
"events": ["extraction.completed", "extraction.failed"],
"secret": "whsec_AbCdEfGhIjKlMnOpQrStUvWxYz123456",
"isActive": true,
"createdAt": "2026-04-14T10:00:00Z"
}Save the secret securely — it is only shown once and is used to verify webhook signatures.
Events
Subscribe to one or more of these event types:
| Event | Description |
|---|---|
| extraction.completed | Fired when a single document extraction finishes successfully. |
| extraction.failed | Fired when a document extraction fails (corrupt file, unsupported format). |
| batch.completed | Fired when all documents in a batch have been processed. |
| batch.failed | Fired when a batch processing job fails. |
| quota.warning | Fired when usage reaches 80% of monthly quota. |
| quota.exceeded | Fired when monthly quota is exceeded. |
Payload Format
Every webhook sends a JSON POST request with this structure:
{
"event": "extraction.completed",
"timestamp": "2026-04-14T10:05:00Z",
"data": {
"id": "ext_7f3a2b1c-4d5e-6f78-9a0b-cdef12345678",
"status": "completed",
"documentType": "invoice",
"confidence": 0.94,
"data": { ... },
"metadata": {
"fileName": "invoice.pdf",
"fileSize": 145230,
"mimeType": "application/pdf"
},
"processingTimeMs": 1240
}
}Headers
| Header | Description |
|---|---|
| Content-Type | application/json |
| X-ParseFlow-Signature | HMAC-SHA256 signature of the request body |
| X-ParseFlow-Event | The event type (e.g., extraction.completed) |
| X-ParseFlow-Delivery | Unique delivery ID for deduplication |
Verification
Always verify webhook signatures to ensure requests are from ParseFlow. Compute an HMAC-SHA256 of the raw request body using your webhook secret:
// Node.js verification example
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// In your webhook handler:
app.post('/webhooks/parseflow', (req, res) => {
const signature = req.headers['x-parseflow-signature'];
const isValid = verifyWebhook(
JSON.stringify(req.body),
signature,
'whsec_your_webhook_secret'
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process the webhook event
const { event, data } = req.body;
console.log(`Received: ${event}`);
res.status(200).json({ received: true });
});Retry Policy
If your endpoint returns a non-2xx status code, ParseFlow retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 2 minutes |
| 3rd retry | 15 minutes |
| 4th retry | 1 hour |
| 5th retry (final) | 4 hours |
After 5 failed attempts, the webhook is marked as failing. It will be automatically disabled after 3 consecutive days of failures.
Managing Webhooks
List Webhooks
curl https://parseflow.dev/api/webhooks/notifications \ -H "X-API-Key: dm_live_your_api_key"
Delete a Webhook
curl -X DELETE "https://parseflow.dev/api/webhooks/notifications?id=wh_a1b2c3d4e5f6g7h8" \ -H "X-API-Key: dm_live_your_api_key"
Example Integration
Complete Python example using Flask to receive and process webhook events:
import hmac
import hashlib
from flask import Flask, request, jsonify
app = Flask(__name__)
WEBHOOK_SECRET = "whsec_your_webhook_secret"
@app.route('/webhooks/parseflow', methods=['POST'])
def handle_webhook():
# 1. Verify signature
signature = request.headers.get('X-ParseFlow-Signature', '')
expected = hmac.new(
WEBHOOK_SECRET.encode(),
request.data,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected):
return jsonify({"error": "Invalid signature"}), 401
# 2. Process event
payload = request.json
event = payload['event']
if event == 'extraction.completed':
doc_id = payload['data']['id']
doc_type = payload['data']['documentType']
# Save to your database, trigger next step, etc.
print(f"Extraction complete: {doc_id} ({doc_type})")
elif event == 'quota.warning':
# Send alert to team
print("Quota warning: 80% usage reached")
return jsonify({"received": True}), 200