Webhooks
Receive real-time notifications when events occur in your Shablonix account.
Overview
Webhooks POST to your HTTPS URL when a PDF generation finishes or fails. Configure them once
in Settings or via the SDK. They fire even when POST /v1/generate already returned the completed file in the same response.
Use them to:
- Build async workflows for document generation
- Sync generated documents to your storage
- Send notifications when documents are ready
- Track document generation analytics
Webhook Setup
You can configure webhooks in two ways:
1. Dashboard Configuration
Navigate to Settings → Webhooks in your dashboard to:
- - Add webhook endpoints
- - Select which events to subscribe to
- - View delivery logs and retry failed deliveries
- - Manage webhook secrets
2. Per-Request Webhooks
Include a webhook_url on a generate call for an
unsigned callback on that job only. Account webhooks stay signed and still fire.
curl -sS -X POST https://api.shablonix.online/v1/generate \
-H "Authorization: Bearer tf_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"template_id": "meridian-invoice",
"data": { "invoice": { "number": "INV-001" } },
"async": true,
"webhook_url": "https://your-app.com/webhooks/generation"
}' generation.completed and are not HMAC-signed. Verify account webhooks with constructWebhookEvent.3. API Configuration
Configure account-level webhooks programmatically using the API:
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/webhooks/settings | Configure webhook endpoint |
GET | /v1/webhooks/settings | Get current webhook configuration |
DELETE | /v1/webhooks/settings | Remove webhook configuration |
POST | /v1/webhooks/settings/test | Send a test webhook event |
import { Shablonix } from '@shablonix/sdk';
const client = new Shablonix(process.env.SHABLONIX_API_KEY!);
const config = await client.configureWebhook({
url: 'https://your-app.com/webhooks/shablonix',
events: ['document.completed', 'document.failed', 'batch.completed'],
});
console.log(config.secret); Requires the webhooks:read and webhooks:write API key scopes.
4. Zapier and Make REST hooks
Account settings keep one URL per user. Zapier and Make subscribe through POST /v1/hooks instead, so turning on a Zap does
not overwrite the production webhook. Events fan out to both.
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/hooks | Register an extra destination (source: zapier or make) |
DELETE | /v1/hooks/:id | Remove that destination (idempotent) |
integrations/.
They POST template_id / format / async: true to https://api.shablonix.online. Status values are
lowercase (completed, not COMPLETED).Use with async generate
Typical production pattern: enqueue a PDF with async: true, then wait for document.completed instead of holding the generate HTTP
call. Polling status_url still works if you cannot receive
POSTs. Full enqueue, poll, and download examples (cURL, Node, SDK) are on Generate PDF → Async generation.
- 1.
POST /v1/webhooks/settingsonce. - 2.
POST /v1/generatewithasync: true. Store theid. - 3. On
document.completed,GETthe payloadfile_urlwith the same API key (ordownloadGenerationBytes(id)in the SDK).
# 1. Account webhook (once)
curl -sS -X POST https://api.shablonix.online/v1/webhooks/settings \
-H "Authorization: Bearer tf_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-app.example/webhooks/shablonix",
"events": ["document.completed", "document.failed"]
}'
# 2. Enqueue
curl -sS -X POST https://api.shablonix.online/v1/generate \
-H "Authorization: Bearer tf_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"template_id": "meridian-invoice",
"data": { "invoice": { "number": "INV-001" } },
"async": true
}'
# 3. After the webhook: download with the generation id
curl -sS https://api.shablonix.online/v1/generate/GENERATION_ID/download \
-H "Authorization: Bearer tf_live_your_api_key" \
--output invoice.pdf Payload Format
Webhook payloads are sent as JSON via HTTP POST requests:
{
"id": "evt_abc123xyz",
"type": "document.completed",
"created_at": "2026-08-25T10:30:00Z",
"api_version": "2024-01-01",
"data": {
"id": "a1b2c3d4",
"status": "completed",
"template_id": "meridian-invoice",
"file_url": "/v1/generate/a1b2c3d4/download",
"status_url": "/v1/generate/a1b2c3d4",
"file_size": 142857,
"render_time_ms": 234
}
} HTTP Headers
Each webhook request includes these headers:
| Header | Description |
|---|---|
Content-Type | Always application/json |
X-Shablonix-Signature | HMAC-SHA256 signature for verification |
X-Shablonix-Timestamp | Unix timestamp when the webhook was sent |
X-Shablonix-Event | Event type (e.g., document.completed) |
X-Shablonix-Delivery-ID | Unique ID for this delivery attempt |
Event Types
Subscribe to specific events based on your needs:
Document Events
| Event | Description |
|---|---|
document.completed | PDF generated successfully. Includes file_url. |
document.failed | Document generation failed |
Batch Events
| Event | Description |
|---|---|
batch.progress | Periodic progress while a batch is running |
batch.completed | Batch finished (some items may have failed) |
batch.failed | Every item in the batch failed |
Signature Verification
Always verify webhook signatures to ensure requests are from Shablonix and haven't been tampered with.
Verification Steps
- 1 Extract the signature from the
X-Shablonix-Signatureheader - 2 Get the timestamp from the
X-Shablonix-Timestampheader - 3 Concatenate the timestamp and raw request body:
{timestamp}.{body} - 4 Compute HMAC-SHA256 using your webhook secret
- 5 Compare signatures using constant-time comparison
Code Examples
import { Shablonix, constructWebhookEvent } from '@shablonix/sdk';
import express from 'express';
const client = new Shablonix(process.env.SHABLONIX_API_KEY!);
const app = express();
app.post(
'/webhooks/shablonix',
express.raw({ type: 'application/json' }),
async (req, res) => {
const event = await constructWebhookEvent(
req.body,
req.headers,
process.env.SHABLONIX_WEBHOOK_SECRET!,
);
if (event.type === 'document.completed') {
const pdf = await client.downloadGenerationBytes(event.data.id);
console.log('stored', pdf.byteLength, 'bytes');
}
res.status(200).json({ received: true });
},
); Retry Policy
Shablonix automatically retries failed webhook deliveries with exponential backoff.
Retry Schedule
| Attempt | Delay | Cumulative Time |
|---|---|---|
| 1 (initial) | Immediate | 0 |
| 2 | 1 minute | 1 minute |
| 3 | 5 minutes | 6 minutes |
| 4 | 30 minutes | 36 minutes |
| 5 | 2 hours | ~2.5 hours |
| 6 | 8 hours | ~10.5 hours |
| 7 (final) | 24 hours | ~34.5 hours |
Success Criteria
A webhook delivery is considered successful when your endpoint:
- - Returns a
2xxstatus code - - Responds within 30 seconds
Data Retention
Generated documents are automatically deleted after 24 hours. Webhook
payloads for completed documents include an expires_at field so your system knows when
the file will no longer be available for download.
Best Practices
1. Respond Quickly
Return a 200 response immediately after receiving the webhook. Process the event asynchronously using a job queue to avoid timeouts.
2. Handle Duplicates
Webhooks may be delivered multiple times. Use the event id to deduplicate.
// Store processed event IDs
const processedEvents = new Set();
app.post('/webhooks/shablonix', (req, res) => {
const event = req.body;
if (processedEvents.has(event.id)) {
return res.status(200).json({ received: true, duplicate: true });
}
processedEvents.add(event.id);
// Process event...
res.status(200).json({ received: true });
}); 3. Always Verify Signatures
Never skip signature verification, even in development. This protects against webhook spoofing attacks.
4. Use HTTPS
Webhook endpoints must use HTTPS. HTTP endpoints are rejected in production environments.
5. Monitor Failures
Set up alerts for webhook failures in your dashboard. Repeated failures may indicate issues with your endpoint.
6. Log Webhook Payloads
Log incoming webhooks for debugging. Include the delivery ID and timestamp for easier troubleshooting.