Webhooks
Webhooks allow you to receive real-time notifications when asynchronous PDF generation completes or fails.
Webhooks only fire for asynchronous requests (using Prefer: respond-async
header). Synchronous requests return PDFs directly.
Two Webhook Systems
doczap supports two webhook systems for flexibility:
System | Configuration | Features | Use Case |
---|---|---|---|
Dashboard System | Dashboard UI | HMAC signatures, advanced filtering, retry management, logs | Recommended |
Legacy System | Request body | Basic retry, custom headers | Backward compatibility |
Both systems can be used simultaneously.
New System (Recommended)
1. Configure in Dashboard
- Go to Webhooks in your dashboard
- Click “Add Webhook”
- Enter your endpoint URL and name
- Select events to subscribe to
- Configure scope (organization-wide or specific templates)
- Set up filters (optional)
- Copy the signing secret for verification
Webhook Filtering
Filter which events trigger your webhook:
Filter | Description | Example |
---|---|---|
Test Mode | Only test or production documents | is_test: false |
Document Size | Min/max file size in KB | min_size_kb: 100, max_size_kb: 5000 |
Template Pattern | Regex pattern for endpoint paths | endpoint_path_pattern: "invoice-*" |
Exclude Errors | Skip specific error codes | exclude_errors: ["TEMPLATE_NOT_FOUND"] |
2. Webhook Events
Event | Description | Payload |
---|---|---|
document.completed | PDF generated successfully | Document data + URL |
document.failed | PDF generation failed | Error details |
3. Webhook Payload
Success Event (document.completed)
{
"id": "evt_abc123xyz",
"type": "document.completed",
"created": 1640995200,
"data": {
"request_id": "req_def456ghi",
"document_id": "doc_xyz789abc",
"organization_id": "org_abc123",
"template_id": "tpl_def456",
"document_url": "https://cdn.doczap.app/outputs/doc_xyz789abc.pdf",
"size_bytes": 245632,
"render_time_ms": 245,
"created_at": "2024-01-07T12:00:00Z",
"completed_at": "2024-01-07T12:00:00.245Z",
"is_test": false
}
}
Failure Event (document.failed)
{
"id": "evt_def456xyz",
"type": "document.failed",
"created": 1640995200,
"data": {
"request_id": "req_ghi789def",
"document_id": "doc_mno345pqr",
"organization_id": "org_abc123",
"template_id": "tpl_def456",
"error": {
"code": "RENDER_TIMEOUT",
"message": "PDF generation timed out after 30 seconds",
"details": {
"timeout_ms": 30000,
"template_size": 1024000
}
},
"created_at": "2024-01-07T12:00:00Z",
"failed_at": "2024-01-07T12:00:30Z",
"is_test": false
}
}
4. Verify Webhook Signatures
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const timestamp = signature.split(',')[0].split('=')[1];
const receivedSig = signature.split(',')[1].split('=')[1];
const expectedSig = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${payload}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(receivedSig),
Buffer.from(expectedSig)
);
}
// Express.js example
app.post('/webhook', express.raw({type: '*/*'}), (req, res) => {
const signature = req.headers['x-doczap-signature'];
const isValid = verifyWebhookSignature(
req.body.toString(),
signature,
process.env.WEBHOOK_SECRET
);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
switch (event.type) {
case 'document.completed':
console.log('PDF ready:', event.data.pdf_url);
break;
case 'document.failed':
console.error('PDF failed:', event.data.error);
break;
}
res.status(200).send('OK');
});
Legacy System
Include webhook configuration in your request:
{
"customer": "Acme Corp",
"amount": 1250.00,
"webhook": {
"url": "https://your-app.com/webhooks/doczap",
"headers": {
"X-Custom-Auth": "your-secret"
},
"retries": 3
}
}
Legacy webhooks don’t include signatures. Use the new system for production.
Retry Logic
Dashboard System
Configurable retry settings per webhook:
Setting | Default | Description |
---|---|---|
Max Retries | 3 | Maximum retry attempts (0-5) |
Timeout | 30s | Request timeout (5-60 seconds) |
Backoff Multiplier | 2 | Exponential backoff factor |
Retry delays with default settings:
Attempt | Base Delay | With Jitter | Total Time |
---|---|---|---|
1 | Immediate | 0s | 0s |
2 | 2s | 1-3s | ~2s |
3 | 4s | 2-6s | ~6s |
4 | 8s | 4-12s | ~14s |
Legacy System
Fixed exponential backoff:
Attempt | Delay | Total Time |
---|---|---|
1 | Immediate | 0s |
2 | ~2s | 2s |
3 | ~4s | 6s |
4 | ~8s | 14s |
5 | ~16s | 30s |
Failed webhooks after all retries are logged and can be manually re-sent from the dashboard.
Webhook Requirements
Your endpoint must:
- Accept POST requests with JSON payload
- Return 2xx status within 30 seconds
- Handle duplicates - Use event ID for idempotency
- Verify signatures - Prevent replay attacks
Testing Webhooks
1. Use Webhook Testing Tools
# 1. Go to https://webhook.site
# 2. Copy your unique URL
# 3. Use it as your webhook endpoint
curl -X POST https://api.doczap.app/api/v1/... \
-H "Prefer: respond-async" \
-d '{
"webhook": {
"url": "https://webhook.site/your-unique-id"
}
}'
2. Test from Dashboard
- Configure a webhook in the dashboard
- Click “Send Test Event”
- Verify your endpoint receives the test payload
Common Issues
Webhook Not Receiving Events
- Check async mode - Webhooks only fire for async requests
- Verify URL - Must be publicly accessible HTTPS
- Check firewall - Allow Doczap IPs (see docs)
- Review logs - Check webhook logs in dashboard
Signature Verification Failing
- Use raw body - Don’t parse JSON before verifying
- Check header name -
X-Doczap-Signature
(case-insensitive) - Verify secret - Must match dashboard exactly
- Handle encoding - UTF-8 encoding required
Duplicate Events
Implement idempotency:
const processedEvents = new Set();
function handleWebhook(event) {
if (processedEvents.has(event.id)) {
return; // Already processed
}
processedEvents.add(event.id);
// Process event...
}
Webhook Logs
View detailed logs for all webhook attempts in the dashboard:
- Request/response details
- HTTP status codes
- Response time
- Retry attempts
- Error messages
Logs are retained for:
- Free plan: 2 days
- Pro plan: 90 days
- Scale plan: 365 days
Best Practices
- Return quickly - Process asynchronously if needed
- Log everything - Include event IDs and timestamps
- Monitor failures - Set up alerts for webhook errors
- Use signatures - Always verify in production
- Handle retries - Make your handler idempotent
- Use filters - Reduce unnecessary webhook calls
- Set appropriate timeouts - Balance reliability and speed
Need help debugging webhooks? Check the webhook logs in your dashboard or contact support with your request ID.