/v1/generateGenerate PDF
Generate a PDF document from a template with dynamic data.
Overview
The generate endpoint creates a PDF document by merging a template with the provided data. The generated PDF is stored temporarily and a download URL is returned in the response.
Documents are available for download for 24 hours. After that, you will need to regenerate the document or store it in your own storage.
Endpoint
POST https://api.shablonix.online/v1/generate
Content-Type: application/json
Authorization: Bearer tf_live_your_api_key Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
template_id | string | Yes | The ID of the template to use for generation |
data | object | Yes | Key-value pairs to populate template variables |
options | object | No | PDF generation options (page size, margins, etc.) |
async | boolean | No | true returns 202 Accepted with status_url. Poll or wait for a webhook. Direct file streaming is disabled. |
webhook_url | string | No | Optional unsigned callback for this job only. Prefer account webhooks for HMAC-signed document.completed events. |
filename | string | No | Custom filename for the generated PDF |
Options
The options object allows you to customize PDF
generation:
Page Size
| Value | Dimensions | Use Case |
|---|---|---|
letter | 8.5 x 11 inches | US standard (default) |
a4 | 210 x 297 mm | International standard |
legal | 8.5 x 14 inches | Legal documents |
custom | Custom width/height | Requires width & height |
Orientation
| Value | Description |
|---|---|
portrait | Vertical orientation (default) |
landscape | Horizontal orientation |
Margins
Margins can be specified in inches, millimeters, or pixels:
{
"options": {
"margins": {
"top": "1in",
"right": "0.75in",
"bottom": "1in",
"left": "0.75in"
}
}
} "margins": "1in"Full Options Example
{
"template_id": "invoice_pro",
"data": { ... },
"options": {
"page_size": "a4",
"orientation": "portrait",
"margins": {
"top": "20mm",
"right": "15mm",
"bottom": "20mm",
"left": "15mm"
},
"header": {
"enabled": true,
"height": "50px"
},
"footer": {
"enabled": true,
"height": "30px",
"content": "Page {{page}} of {{pages}}"
},
"print_background": true,
"scale": 1.0
}
} Response
A successful request returns a JSON object with the generated document details:
{
"id": "a1b2c3d4",
"status": "completed",
"template_id": "meridian-invoice",
"file_url": "/v1/generate/a1b2c3d4/download",
"status_url": "/v1/generate/a1b2c3d4",
"file_size": 142857,
"created_at": "2026-08-25T10:30:00Z",
"expires_at": "2026-08-28T10:30:00Z",
"render_time_ms": 234
} | Field | Type | Description |
|---|---|---|
id | string | Unique document identifier |
status | string | completed, processing, or failed |
file_url | string | Authenticated download URL when the job has completed |
file_size | integer | File size in bytes |
status_url | string | Stable polling URL for this generation |
expires_at | string | ISO 8601 timestamp when URL expires |
Code Examples
curl -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": {
"company_name": "Acme Corp",
"invoice_number": "INV-2024-001",
"date": "January 15, 2024",
"due_date": "February 15, 2024",
"customer": {
"name": "John Smith",
"email": "john@example.com",
"address": "123 Main St, New York, NY 10001"
},
"items": [
{
"description": "Web Development Services",
"quantity": 40,
"unit_price": 150,
"total": 6000
},
{
"description": "UI/UX Design",
"quantity": 20,
"unit_price": 125,
"total": 2500
}
],
"subtotal": 8500,
"tax_rate": 8.5,
"tax_amount": 722.50,
"total": 9222.50
},
"options": {
"page_size": "letter",
"orientation": "portrait"
}
}' Async generation
Synchronous generate waits until the PDF is stored, then returns JSON with file_url. Set async: true when you do not want to hold the HTTP
connection: the API returns 202 Accepted and you learn
the result by polling, by webhook, or both.
The same REST paths work from cURL, any HTTP client, or @shablonix/sdk. You do not need the SDK.
- Poll —
GETthestatus_urluntilcompletedorfailed. - Account webhooks — configure once. Shablonix
POSTs signed
document.completed/document.failedwhen the file is ready, including after a synchronous generate. - Per-request
webhook_url— extra unsigned callback for that job only.
Accept: application/pdf streaming is disabled when async or webhook_url is set. Download from file_url after the job completes.Enqueue
POST the same body as a sync generate, plus "async": true. Store id and status_url from the 202 body.
{
"id": "a1b2c3d4",
"status": "pending",
"template_id": "meridian-invoice",
"created_at": "2026-08-26T10:30:00Z",
"expires_at": "2026-08-29T10:30:00Z",
"status_url": "/v1/generate/a1b2c3d4"
} 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-2026-001" } },
"async": true
}' Poll status
GET /v1/generate/:id (the status_url). Status is pending, processing, completed, or failed. Prefix relative URLs with https://api.shablonix.online.
ID=a1b2c3d4
while true; do
BODY=$(curl -sS https://api.shablonix.online/v1/generate/$ID \
-H "Authorization: Bearer tf_live_your_api_key")
STATUS=$(printf '%s' "$BODY" | python3 -c 'import json,sys; print(json.load(sys.stdin)["status"])')
echo "$STATUS"
case "$STATUS" in
completed|failed) printf '%s\n' "$BODY"; break ;;
esac
sleep 2
done Download
When status is completed, GET the file_url with the same API key.
Copy the bytes into your own storage before expires_at.
curl -sS https://api.shablonix.online/v1/generate/$ID/download \
-H "Authorization: Bearer tf_live_your_api_key" \
--output invoice.pdf Account webhooks
Configure one HTTPS URL. Every later generate — sync or async — delivers document.completed or document.failed when those events are selected.
Payloads are HMAC-signed. Requires webhooks:write. Store the secret; it is shown once if Shablonix generated it.
Zapier and Make use POST /v1/hooks instead — extra
destinations that do not replace this account URL. See Webhooks → REST hooks.
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"]
}' Receive the POST, verify the signature, then download with the generation id. file_url and status_url in the payload are API-relative.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const API = 'https://api.shablonix.online';
app.post(
'/webhooks/shablonix',
express.raw({ type: 'application/json' }),
async (req, res) => {
const signature = req.get('X-Shablonix-Signature');
const timestamp = req.get('X-Shablonix-Timestamp');
const secret = process.env.SHABLONIX_WEBHOOK_SECRET;
const signed = `${timestamp}.${req.body.toString('utf8')}`;
const expected = crypto.createHmac('sha256', secret).update(signed).digest('hex');
if (
!signature ||
!timestamp ||
!crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'))
) {
return res.status(400).json({ error: 'invalid signature' });
}
const event = JSON.parse(req.body.toString('utf8'));
if (event.type === 'document.completed') {
const pdf = await fetch(`${API}${event.data.file_url}`, {
headers: { Authorization: `Bearer ${process.env.SHABLONIX_API_KEY}` },
});
// store Buffer.from(await pdf.arrayBuffer())
}
res.status(200).json({ received: true });
},
); Headers, retry schedule, and replay protection are on the Webhooks page.
Per-request webhook
Pass webhook_url on one generate call for an extra POST
when that job finishes. These callbacks are not HMAC-signed. Account webhooks still fire if configured.
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-2026-001" } },
"async": true,
"webhook_url": "https://your-app.example/webhooks/generation"
}' Batch
POST /v1/generate/batch is always asynchronous
(202 Accepted, 1–100 items). Poll GET /v1/generate/batch/:id or subscribe to batch.progress, batch.completed, and batch.failed.
curl -sS -X POST https://api.shablonix.online/v1/generate/batch \
-H "Authorization: Bearer tf_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"template_id": "meridian-invoice",
"format": "pdf",
"items": [
{ "data": { "invoice": { "number": "INV-001" } } },
{ "data": { "invoice": { "number": "INV-002" } } }
]
}'