Shablonix

Webhooks

Receive real-time notifications when events occur in your Shablonix account.

Overview

Webhooks allow you to receive HTTP callbacks when specific events occur, such as when a document is generated or a template is updated. This enables you 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 SettingsWebhooks 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 in your API request to receive a callback for that specific operation:

			
				{
  "template_id": "tpl_invoice_basic",
  "data": { ... },
  "webhook_url": "https://your-app.com/webhooks/shablonix",
  "webhook_secret": "your_webhook_secret"
}
			
		
Per-request webhooks are useful for async document generation when you need to track individual requests.

3. API Configuration

Configure account-level webhooks programmatically using the API:

MethodEndpointDescription
POST/v1/webhooks/settingsConfigure webhook endpoint
GET/v1/webhooks/settingsGet current webhook configuration
DELETE/v1/webhooks/settingsRemove webhook configuration
POST/v1/webhooks/settings/testSend a test webhook event
			
				# Configure account webhook
curl -X POST https://api.shablonix.com/v1/webhooks/settings \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/shablonix",
    "events": ["document.completed", "document.failed", "batch.completed"]
  }'

# Response includes auto-generated secret if not provided:
# {
#   "configured": true,
#   "url": "https://your-app.com/webhooks/shablonix",
#   "secret": "a1b2c3d4...",
#   "events": ["document.completed", "document.failed", "batch.completed"],
#   "created_at": "2024-01-15T10:00:00.000Z",
#   "updated_at": "2024-01-15T10:00:00.000Z"
# }
			
		

Requires the webhooks:read and webhooks:write API key scopes.

Payload Format

Webhook payloads are sent as JSON via HTTP POST requests:

			
				{
  "id": "evt_abc123xyz",
  "type": "document.completed",
  "created_at": "2024-01-15T10:30:00Z",
  "data": {
    "id": "doc_7f3k9x2m1n",
    "object": "document",
    "status": "completed",
    "template_id": "tpl_invoice_basic",
    "pdf_url": "https://cdn.shablonix.com/docs/doc_7f3k9x2m1n.pdf",
    "pdf_size": 142857,
    "page_count": 2,
    "created_at": "2024-01-15T10:30:00Z",
    "expires_at": "2024-01-16T10:30:00Z",
    "metadata": {
      "render_time_ms": 234,
      "request_id": "req_xyz789"
    }
  },
  "api_version": "2024-01-01"
}
			
		

HTTP Headers

Each webhook request includes these headers:

HeaderDescription
Content-TypeAlways application/json
X-Shablonix-SignatureHMAC-SHA256 signature for verification
X-Shablonix-TimestampUnix timestamp when the webhook was sent
X-Shablonix-EventEvent type (e.g., document.completed)
X-Shablonix-Delivery-IDUnique ID for this delivery attempt

Event Types

Subscribe to specific events based on your needs:

Document Events

EventDescription
document.processingDocument generation has started
document.completedDocument generated successfully
document.failedDocument generation failed

Template Events

EventDescription
template.createdNew template created
template.updatedTemplate updated (new version)
template.deletedTemplate deleted

Signature Verification

Always verify webhook signatures to ensure requests are from Shablonix and haven't been tampered with.

Verification Steps

  1. 1 Extract the signature from the X-Shablonix-Signature header
  2. 2 Get the timestamp from the X-Shablonix-Timestamp header
  3. 3 Concatenate the timestamp and raw request body: {timestamp}.{body}
  4. 4 Compute HMAC-SHA256 using your webhook secret
  5. 5 Compare signatures using constant-time comparison

Code Examples

			
				import crypto from 'crypto';

function verifyWebhookSignature(payload, headers, secret) {
  const signature = headers['x-shablonix-signature'];
  const timestamp = headers['x-shablonix-timestamp'];

  // Check timestamp to prevent replay attacks (5 min tolerance)
  const currentTime = Math.floor(Date.now() / 1000);
  if (Math.abs(currentTime - parseInt(timestamp)) > 300) {
    throw new Error('Webhook timestamp too old');
  }

  // Compute expected signature
  const signedPayload = `${timestamp}.${payload}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  // Constant-time comparison
  const signatureBuffer = Buffer.from(signature, 'hex');
  const expectedBuffer = Buffer.from(expectedSignature, 'hex');

  if (!crypto.timingSafeEqual(signatureBuffer, expectedBuffer)) {
    throw new Error('Invalid webhook signature');
  }

  return true;
}

// Express.js example
app.post('/webhooks/shablonix', express.raw({ type: 'application/json' }), (req, res) => {
  try {
    verifyWebhookSignature(
      req.body.toString(),
      req.headers,
      process.env.SHABLONIX_WEBHOOK_SECRET
    );

    const event = JSON.parse(req.body);

    switch (event.type) {
      case 'document.completed':
        console.log('Document ready:', event.data.pdf_url);
        break;
      case 'document.failed':
        console.error('Generation failed:', event.data.error);
        break;
    }

    res.status(200).json({ received: true });
  } catch (error) {
    console.error('Webhook error:', error.message);
    res.status(400).json({ error: error.message });
  }
});
			
		

Retry Policy

Shablonix automatically retries failed webhook deliveries with exponential backoff.

Retry Schedule

AttemptDelayCumulative Time
1 (initial)Immediate0
21 minute1 minute
35 minutes6 minutes
430 minutes36 minutes
52 hours~2.5 hours
68 hours~10.5 hours
7 (final)24 hours~34.5 hours

Success Criteria

A webhook delivery is considered successful when your endpoint:

  • - Returns a 2xx status code
  • - Responds within 30 seconds
If your endpoint does not respond within 30 seconds, the request is considered failed and will be retried.

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.

Always download generated files as soon as you receive the webhook notification. After expiration, files are permanently deleted and cannot be recovered. See the Data Retention page for full details and best practices.

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.