Skip to main content

Templates

Templates let you separate your document structure from your data. Store a document definition as a template once, then generate personalized PDFs by passing different data each time.

Template workflow​

1. Create template (POST /templates)
|
v
2. Store template in database
|
v
3. Generate PDF with data (POST /documents/generate-from-template)
|
v
4. Receive personalized PDF

Step 1: Create a template​

A template is a document definition containing $data references and/or source bindings. Save it via the Templates API.

{
"name": "Shipping Invoice",
"content": "{\"document\":{\"styles\":{\"title\":{\"fontSize\":20,\"bold\":true},\"body\":{\"fontSize\":10}},\"contents\":[{\"p\":\"$data.document.title\",\"style\":\"title\"},{\"p\":\"Prepared for $data.client.companyName\"},{\"table\":{\"widths\":[3,1,1],\"rows\":[[{\"p\":\"[b]Item[/b]\"},{\"p\":\"[b]Qty[/b]\"},{\"p\":\"[b]Price[/b]\"}]]},\"source\":\"$data.lineItems\",\"map\":[\"item\",\"qty\",\"price\"]},{\"p\":\"Total: [b]$data.invoice.total[/b]\"}]}}"
}

The content field holds the JSON document definition as a string. The name field is a human-readable label.

Step 2: Generate with data​

Call the generate-from-template endpoint with the template ID and your data object.

YOUR_ACCESS_TOKEN is the access token you get by exchanging your API key's Client ID and secret; see Authentication.

curl -X POST https://api.docpayload.com/v1/{tenantId}/documents/generate-from-template \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"templateId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"data": {
"document": {
"title": "Invoice #INV-2026-0330"
},
"client": {
"companyName": "Meridian Logistics"
},
"lineItems": [
{ "item": "Express Shipping - Chicago to Toronto", "qty": "3", "price": "$450" },
{ "item": "Customs Clearance Fee", "qty": "1", "price": "$75" },
{ "item": "Insurance - Premium Coverage", "qty": "3", "price": "$120" }
],
"invoice": {
"total": "$645"
}
}
}'

Step 3: Receive the PDF​

The response includes a download URL and metadata about the generated document.

What gets resolved​

When generating from a template, DocPayload resolves:

FeatureDescription
Scalar references$data.path.to.value in paragraph text
Inline formatting refs$data refs inside [b], [color], etc.
Form field defaults$data refs in [textfield], [checkbox], [textarea] default values
Table sourcePopulates table rows from object arrays
List sourcePopulates list items from string arrays

Real-world example: invoice template​

This complete example shows a realistic invoice template that combines scalar references, a data-driven table, and a data-driven list.

Template definition​

{
"document": {
"metadata": {
"title": "Invoice",
"author": "ShipForge"
},
"pageSetup": {
"size": "A4",
"orientation": "portrait",
"margin": [50, 40, 50, 40]
},
"styles": {
"title": { "fontSize": 20, "fontWeight": "bold" },
"subtitle": { "fontSize": 12, "fontStyle": "italic", "color": "#555555" },
"total": { "fontWeight": "bold", "fontSize": 14 },
"footer": { "fontSize": 8, "color": "#999999", "textAlign": "center" }
},
"header": {
"content": [
{ "p": "[image, /assets/shipforge-logo.png, 80|24][tab, 400, 0]INVOICE", "style": "footer" }
],
"hr": { "borderStyle": "solid", "height": 0.5, "color": "#CCCCCC" },
"skipPages": [1]
},
"footer": {
"content": [
{ "p": "Page $global.PAGE of $global.NUMPAGES", "style": "footer" }
]
},
"content": [
{ "p": "$data.document.title", "style": "title" },
{ "p": "Date: $data.document.date", "style": "subtitle" },
{
"hr": {
"borderStyle": "solid",
"height": 1,
"color": "#000000"
}
},
{
"columns": {
"widths": [1, 1],
"gap": 20,
"rows": [
[
{ "p": "[b]From:[/b] $data.company.name" },
{ "p": "$data.company.address" },
{ "p": "$data.company.city" }
],
[
{ "p": "[b]To:[/b] $data.client.companyName" },
{ "p": "$data.client.address" },
{ "p": "$data.client.city" }
]
]
}
},
{
"table": {
"source": "$data.lineItems",
"map": ["service", "hours", "rate", "total"],
"widths": [4, 1, 1, 1],
"rows": [
[
{ "p": "[b]Service[/b]" },
{ "p": "[b]Hours[/b]" },
{ "p": "[b]Rate[/b]" },
{ "p": "[b]Total[/b]" }
]
]
}
},
{ "p": "Grand Total: $data.invoice.grandTotal", "style": "total" },
{ "p": "" },
{ "p": "Payment Terms:" },
{
"ol": {
"source": "$data.paymentTerms"
}
},
{ "p": "" },
{ "p": "Thank you for your business, $data.client.contactName." }
]
}
}

Data object​

{
"document": {
"title": "Professional Services Invoice",
"date": "March 29, 2026"
},
"company": {
"name": "ShipForge Ltd",
"address": "45 Innovation Drive",
"city": "London, UK"
},
"client": {
"companyName": "Meridian Logistics",
"contactName": "Sarah Mitchell",
"address": "742 Evergreen Terrace",
"city": "Denver, CO"
},
"lineItems": [
{ "service": "Platform setup", "hours": "80", "rate": "$150", "total": "$12,000" },
{ "service": "API integration", "hours": "40", "rate": "$175", "total": "$7,000" },
{ "service": "User training", "hours": "16", "rate": "$125", "total": "$2,000" }
],
"invoice": {
"grandTotal": "$21,000"
},
"paymentTerms": [
"Wire transfer to ShipForge Ltd account",
"Reference: INV-SF-2026-0329",
"Net 30 days from invoice date"
]
}

Generated output​

The same template, called with different data objects, produces personalized invoices for each client -- different names, addresses, line items, and totals -- all from a single stored template.

Composing templates from components​

Templates compose: extract shared headers, footers, and common blocks into reusable components and pull them into any template with use, instead of duplicating them across templates.

See Composition for the full guide — defining components, referencing them, data scoping, style cascade, merge rules, and multi-section assembly.

Template revisions​

Templates support versioning through revisions. When you update a template, the previous version is retained as a parent revision. See the Templates API for revision management endpoints.

Test data​

Templates support an optional testData field that stores sample data alongside the template. This is used in the Playground for previewing the template without requiring a separate data payload.

{
"name": "Shipping Invoice",
"content": "{ ... document definition ... }",
"testData": "{ \"client\": { \"companyName\": \"Test Corp\" }, ... }"
}