Dynamic Data
Inject dynamic data into your PDF templates
Dynamic data allows you to generate unique PDFs from a single template. Instead of hardcoding values into your HTML, you define variables that get replaced with real data at generation time.
Variable Syntax
Use double curly braces to insert variables into your template:
<h1>Hello, {{customerName}}!</h1>
<p>Your order #{{orderId}} was placed on {{orderDate}}.</p>
Then pass the corresponding values in the data field of your API request:
{
"html": "<h1>Hello, {{customerName}}!</h1><p>Your order #{{orderId}} was placed on {{orderDate}}.</p>",
"data": {
"customerName": "Maria Schmidt",
"orderId": "ORD-2026-1847",
"orderDate": "17 February 2026"
}
}
The rendered PDF will display:
Hello, Maria Schmidt! Your order #ORD-2026-1847 was placed on 17 February 2026.
The Data Object
The data field accepts a JSON object. Values can be strings, numbers, booleans, nested objects, or arrays:
{
"data": {
"name": "Max Mustermann",
"age": 34,
"isPremium": true,
"address": {
"street": "Musterstrasse 1",
"city": "Berlin",
"zip": "10115"
},
"items": [
{ "name": "Widget A", "price": 29.99 },
{ "name": "Widget B", "price": 49.99 }
]
}
}
Nested Objects
Access nested properties using dot notation:
<div class="address">
<p>{{address.street}}</p>
<p>{{address.zip}} {{address.city}}</p>
</div>
With the data above, this renders:
Musterstrasse 1 10115 Berlin
You can nest as deeply as needed:
<p>{{company.billing.contact.email}}</p>
Working with Arrays
Arrays are commonly used for line items in invoices, lists of products, or any repeating content. Use the {{#each}} block helper to iterate over arrays:
<table>
<thead>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{{#each items}}
<tr>
<td>{{this.name}}</td>
<td>{{this.price}} EUR</td>
</tr>
{{/each}}
</tbody>
</table>
With this data:
{
"data": {
"items": [
{ "name": "Web Design", "price": "1,200.00" },
{ "name": "Development", "price": "3,400.00" },
{ "name": "Hosting (1 year)", "price": "240.00" }
]
}
}
The template produces a table with three rows, one for each item.
Conditional Rendering
Use {{#if}} blocks to show or hide content based on data values:
{{#if isPremium}}
<div class="premium-badge">Premium Customer</div>
{{/if}}
<h2>{{name}}</h2>
{{#if discount}}
<p class="discount">You receive a {{discount}}% discount on this order.</p>
{{/if}}
You can also use {{else}} for fallback content:
{{#if companyName}}
<p>{{companyName}}</p>
{{else}}
<p>{{firstName}} {{lastName}}</p>
{{/if}}
Complete Example
Here is a full API request demonstrating nested data, arrays, and conditionals:
curl -X POST https://api.docreate.io/api/pdf/external \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"html": "<div class=\"doc\"><h1>Order Confirmation</h1><p>Dear {{customer.name}},</p><p>Thank you for your order <strong>#{{orderId}}</strong> placed on {{orderDate}}.</p>{{#if customer.company}}<p>Company: {{customer.company}}</p>{{/if}}<h2>Shipping Address</h2><p>{{customer.address.street}}<br>{{customer.address.zip}} {{customer.address.city}}<br>{{customer.address.country}}</p><h2>Order Items</h2><table><thead><tr><th>Item</th><th>Qty</th><th>Price</th></tr></thead><tbody>{{#each items}}<tr><td>{{this.name}}</td><td>{{this.quantity}}</td><td>{{this.price}} EUR</td></tr>{{/each}}</tbody></table><div class=\"total\"><p><strong>Total: {{total}} EUR</strong></p></div>{{#if notes}}<div class=\"notes\"><h3>Notes</h3><p>{{notes}}</p></div>{{/if}}</div>",
"css": "body { font-family: Helvetica, sans-serif; font-size: 12px; color: #333; padding: 40px; } h1 { color: #111; } table { width: 100%; border-collapse: collapse; margin: 16px 0; } th, td { padding: 8px 12px; border-bottom: 1px solid #eee; text-align: left; } th { background: #f5f5f5; font-weight: 600; } .total { text-align: right; font-size: 16px; margin-top: 16px; padding-top: 12px; border-top: 2px solid #111; } .notes { margin-top: 32px; padding: 16px; background: #fafafa; border-left: 3px solid #ccc; }",
"data": {
"orderId": "ORD-2026-1847",
"orderDate": "17 February 2026",
"customer": {
"name": "Maria Schmidt",
"company": "Schmidt & Partner GmbH",
"address": {
"street": "Friedrichstrasse 42",
"zip": "10117",
"city": "Berlin",
"country": "Germany"
}
},
"items": [
{ "name": "Premium Plan (Annual)", "quantity": 1, "price": "588.00" },
{ "name": "Extra Storage (100 GB)", "quantity": 2, "price": "48.00" },
{ "name": "Priority Support", "quantity": 1, "price": "120.00" }
],
"total": "804.00",
"notes": "Payment due within 14 days. Thank you for your business."
}
}'
Tips
- Format data before sending. Dates, currency values, and numbers should be pre-formatted in your data object. DoCreate renders exactly what you provide.
- Use meaningful variable names. Names like
{{customerName}}are clearer than{{cn}}and make templates easier to maintain. - Keep templates and data separate. Store your template HTML/CSS independently and only vary the
datafield per request. This makes it easy to update the design without changing your application code. - Test with sample data. Before deploying to production, generate a few test PDFs with representative data to catch formatting issues.
Next Steps
- Headers & Footers -- Add page headers and footers with dynamic content
- Best Practices -- Optimize your templates for production use