We use cookies to improve your experience and analyze site traffic. See our Privacy Policy for details.
Not available yet
ParseFlow does not deliver webhooks today. POST /api/webhooks/notifications returns 501 WEBHOOKS_NOT_AVAILABLE rather than registering an endpoint that would never receive an event. If you registered one earlier, it never fired and can be removed with the DELETE call below.
Until delivery ships, poll GET /api/v1/documents/{id} (single) or the results endpoints after an extraction, and read the per-file array returned by POST /api/v1/batch. Everything below describes the planned interface and is kept so integrations can be written against it in advance — it is not live.
The planned interface: HTTP POST notifications when events happen in your ParseFlow account, removing the need to poll.
Webhooks send HTTP POST requests to your specified URL when events occur. This is ideal for:
Plan requirement: when webhooks ship they are planned for Pro and Enterprise. They are not part of any plan today and are not billed for — see what each plan actually includes.
Planned call. Today this returns 501 WEBHOOKS_NOT_AVAILABLE and registers nothing:
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"]
}'{
"error": "Webhook delivery is not available yet. ...",
"code": "WEBHOOKS_NOT_AVAILABLE"
}{
"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.
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. |
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
}
}| 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 |
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 });
});No deliveries are attempted today, so nothing is ever retried — silence at your endpoint is not a backoff window. The schedule below is the intended behaviour once delivery ships: 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.
curl https://parseflow.dev/api/webhooks/notifications \ -H "X-API-Key: dm_live_your_api_key"
curl -X DELETE "https://parseflow.dev/api/webhooks/notifications?id=wh_a1b2c3d4e5f6g7h8" \ -H "X-API-Key: dm_live_your_api_key"
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