210 × 297 mm
Invoices
Standard invoice
A clean A4 invoice for service businesses, rendered from the EN 16931 invoice model: itemised lines with units, VAT computed from the line totals, payment details with a pay-by-link QR code, page numbers, and the same data embedded as ZUGFeRD / Factur-X EN 16931 XML.
Opens Dynamic Document API with this template preloaded in a free workspace.
Template language features
- Loops
- Conditionals
- Macros
- Filters
- Currency formatting
- Date formatting
- Number formatting
- QR code
- Repeating table headers
- Header & footer
- Page numbers
Filters such as format_currency and qrcode are described in the template language reference.
Variables
Derived from the sample data: 4 top-level fields, 63 paths in total. Send the same shape as data.
| Path | Type | Example |
|---|---|---|
| invoice | object | |
| invoice.number | string | "BF-2026-0147" |
| invoice.issue_date | date | "2026-09-14" |
| invoice.due_date | date | "2026-10-14" |
| invoice.currency | string | "GBP" |
| invoice.buyer_reference | string | "ORR-MKT-2291" |
| invoice.order_reference | string | "PO-44812" |
| invoice.payment_terms | string | "30 days net" |
| invoice.delivery | object | |
| invoice.delivery.date | date | "2026-09-11" |
| invoice.seller | object | |
| invoice.seller.name | string | "Brambleworth & Finch Design Studio" |
| invoice.seller.vat_id | string | "GB123456789" |
| invoice.seller.electronic_address | object | |
| invoice.seller.electronic_address.id | string | "accounts@brambleworth.example.com" |
| invoice.seller.electronic_address.scheme | string | "EM" |
| invoice.seller.address | object | |
| invoice.seller.address.line1 | string | "Unit 4, Tannery Yard" |
| invoice.seller.address.line2 | string | "14 Wharf Lane" |
| invoice.seller.address.city | string | "Bristol" |
| invoice.seller.address.postcode | string | "BS1 6XY" |
| invoice.seller.address.country | string | "GB" |
| invoice.seller.contact | object | |
| invoice.seller.contact.name | string | "Accounts team" |
| invoice.seller.contact.phone | string | "+44 117 496 0321" |
| invoice.seller.contact.email | string | "accounts@brambleworth.example.com" |
| invoice.buyer | object | |
| invoice.buyer.name | string | "Orrery Instruments Ltd" |
| invoice.buyer.vat_id | string | "GB987654321" |
| invoice.buyer.electronic_address | object | |
| invoice.buyer.electronic_address.id | string | "invoices@orrery-instruments.example.com" |
| invoice.buyer.electronic_address.scheme | string | "EM" |
| invoice.buyer.address | object | |
| invoice.buyer.address.line1 | string | "Floor 2, Mercator House" |
| invoice.buyer.address.line2 | string | "27 Alderney Street" |
| invoice.buyer.address.city | string | "Leeds" |
| invoice.buyer.address.postcode | string | "LS2 8QR" |
| invoice.buyer.address.country | string | "GB" |
| invoice.buyer.contact | object | |
| invoice.buyer.contact.name | string | "Priya Castellane" |
| invoice.payment | object | |
| invoice.payment.means_code | string | "30" |
| invoice.payment.remittance_information | string | "BF-2026-0147" |
| invoice.payment.credit_transfers | array<object> · 1 | |
| invoice.payment.credit_transfers[].account_id | string | "GB52EXMP12345612345678" |
| invoice.payment.credit_transfers[].account_name | string | "Brambleworth & Finch Design Studio" |
| invoice.payment.credit_transfers[].bic | string | "EXMPGB2L" |
| invoice.lines | array<object> · 5 | |
| invoice.lines[].quantity | string | "2" |
| invoice.lines[].unit | string | "DAY" |
| invoice.lines[].price | object | |
| invoice.lines[].price.net | string | "850.00" |
| invoice.lines[].tax | object | |
| invoice.lines[].tax.category | string | "S" |
| invoice.lines[].tax.rate | string | "20" |
| invoice.lines[].item | object | |
| invoice.lines[].item.name | string | "Brand identity refresh — discovery workshops" |
| invoice.lines[].item.description | string | "Two half-day workshops and six stakeholder int…" |
| invoice.notes | array<object> · 2 | |
| invoice.notes[].text | string | "Thank you for working with us on the autumn ra…" |
| website | string | "brambleworth.example.com" |
| bank_name | string | "Westcombe Mutual Bank" |
| payment_link | string | "https://pay.example.com/i/BF-2026-0147" |
Sample data
Fictional records used for previews. Replace them with your own data when you call the API.
{
"invoice": {
"number": "BF-2026-0147",
"issue_date": "2026-09-14",
"due_date": "2026-10-14",
"currency": "GBP",
"buyer_reference": "ORR-MKT-2291",
"order_reference": "PO-44812",
"payment_terms": "30 days net",
"delivery": {
"date": "2026-09-11"
},
"seller": {
"name": "Brambleworth & Finch Design Studio",
"vat_id": "GB123456789",
"electronic_address": {
"id": "accounts@brambleworth.example.com",
"scheme": "EM"
},
"address": {
"line1": "Unit 4, Tannery Yard",
"line2": "14 Wharf Lane",
"city": "Bristol",
"postcode": "BS1 6XY",
"country": "GB"
},
"contact": {
"name": "Accounts team",
"phone": "+44 117 496 0321",
"email": "accounts@brambleworth.example.com"
}
},
"buyer": {
"name": "Orrery Instruments Ltd",
"vat_id": "GB987654321",
"electronic_address": {
"id": "invoices@orrery-instruments.example.com",
"scheme": "EM"
},
"address": {
"line1": "Floor 2, Mercator House",
"line2": "27 Alderney Street",
"city": "Leeds",
"postcode": "LS2 8QR",
"country": "GB"
},
"contact": {
"name": "Priya Castellane"
}
},
"payment": {
"means_code": "30",
"remittance_information": "BF-2026-0147",
"credit_transfers": [
{
"account_id": "GB52EXMP12345612345678",
"account_name": "Brambleworth & Finch Design Studio",
"bic": "EXMPGB2L"
}
]
},
"lines": [
{
"quantity": "2",
"unit": "DAY",
"price": { "net": "850.00" },
"tax": { "category": "S", "rate": "20" },
"item": {
"name": "Brand identity refresh — discovery workshops",
"description": "Two half-day workshops and six stakeholder interviews"
}
},
{
"quantity": "1",
"unit": "LS",
"price": { "net": "2400.00" },
"tax": { "category": "S", "rate": "20" },
"item": {
"name": "Logo system and usage guidelines",
"description": "Primary and secondary marks, 24-page guideline document"
}
},
{
"quantity": "12",
"unit": "C62",
"price": { "net": "95.00" },
"tax": { "category": "S", "rate": "20" },
"item": {
"name": "Packaging illustrations",
"description": "Twelve spot illustrations, print-ready vector files"
}
},
{
"quantity": "6.5",
"unit": "HUR",
"price": { "net": "70.00" },
"tax": { "category": "S", "rate": "20" },
"item": {
"name": "Print production supervision"
}
},
{
"quantity": "1",
"unit": "LS",
"price": { "net": "185.40" },
"tax": { "category": "S", "rate": "20" },
"item": {
"name": "Colour proofing",
"description": "Third-party costs, passed through at cost"
}
}
],
"notes": [
{ "text": "Thank you for working with us on the autumn range." },
{ "text": "Questions about this invoice? Reply to accounts@brambleworth.example.com." }
]
},
"website": "brambleworth.example.com",
"bank_name": "Westcombe Mutual Bank",
"payment_link": "https://pay.example.com/i/BF-2026-0147"
}Source
The body markup with Jinja, the stylesheet placed in <head> and the header and footer settings.
{#- Standard invoice rendered from the EN 16931 invoice model (`invoice`, proto/einvoice/invoice.schema.json).
With `output.einvoice.invoice_path = "invoice"` (this template's default) the renderer embeds the same data as
Factur-X / ZUGFeRD XML; the platform computes the amounts as `render.invoice` for every render of it (with or
without the XML, proto/README.md §20.7), so the page prints exactly the amounts of the XML. `render.einvoice`
only tells whether the XML is embedded. Without either (e.g. an HTML render of the file) the template applies
the same rules itself.
`website`, `bank_name` and `payment_link` are display-only extras outside the invoice model. -#}
{%- set inv = invoice -%}
{%- set cur = inv.currency -%}
{%- set loc = "en-GB" -%}
{%- set embedded = render.einvoice if (render is defined and render.einvoice is defined and render.einvoice) else none -%}
{%- set computed = render.invoice if (render is defined and render.invoice is defined and render.invoice) else embedded -%}
{%- set units = {"HUR": ["hour", "hours"], "DAY": ["day", "days"], "WEE": ["week", "weeks"], "MON": ["month", "months"], "C62": ["item", "items"], "H87": ["piece", "pieces"], "LS": ["lump sum", "lump sums"], "E48": ["service", "services"], "D68": ["word", "words"], "ZP": ["page", "pages"], "KGM": ["kg", "kg"], "MTR": ["m", "m"], "LTR": ["l", "l"]} -%}
{%- set countries = {"GB": "United Kingdom", "IE": "Ireland", "DE": "Germany", "FR": "France", "NL": "Netherlands", "BE": "Belgium", "ES": "Spain", "IT": "Italy", "AT": "Austria", "US": "United States"} -%}
{%- set profiles = {"MINIMUM": "Factur-X MINIMUM", "BASIC_WL": "Factur-X BASIC WL", "BASIC": "ZUGFeRD / Factur-X BASIC", "EN16931": "ZUGFeRD / Factur-X EN 16931", "EXTENDED": "ZUGFeRD / Factur-X EXTENDED", "XRECHNUNG": "XRechnung"} -%}
{%- set exemptions = {"Z": "Zero rated", "E": "Exempt from VAT", "AE": "Reverse charge", "K": "Intra-community supply", "G": "Export outside the EU", "O": "Outside the scope of VAT"} -%}
{#- Line net amounts and VAT groups (EN 16931: line net = quantity × price ÷ base quantity − allowances + charges) -#}
{%- set ns = namespace(rows=[], nets=[], line_total=0.0, allowance_total=0.0, charge_total=0.0) -%}
{%- for line in inv.lines -%}
{%- set base = (line.price.base_quantity | default(1)) | float -%}
{%- set lns = namespace(net=((line.quantity | float) * (line.price.net | float) / base) | round(2)) -%}
{%- for a in line.allowances | default([]) -%}{%- set lns.net = lns.net - (a.amount | float) -%}{%- endfor -%}
{%- for c in line.charges | default([]) -%}{%- set lns.net = lns.net + (c.amount | float) -%}{%- endfor -%}
{%- set net = (computed.lines[loop.index0].net_amount | float) if computed else (lns.net | round(2)) -%}
{%- set rate = (line.tax.rate | default(0)) | float -%}
{%- set ns.nets = ns.nets + [net] -%}
{%- set ns.rows = ns.rows + [{"key": line.tax.category ~ "|" ~ rate, "category": line.tax.category, "rate": rate, "net": net}] -%}
{%- set ns.line_total = ns.line_total + net -%}
{%- endfor -%}
{%- for a in inv.allowances | default([]) -%}
{%- set rate = (a.tax_rate | default(0)) | float -%}
{%- set ns.rows = ns.rows + [{"key": a.tax_category ~ "|" ~ rate, "category": a.tax_category, "rate": rate, "net": 0 - (a.amount | float)}] -%}
{%- set ns.allowance_total = ns.allowance_total + (a.amount | float) -%}
{%- endfor -%}
{%- for c in inv.charges | default([]) -%}
{%- set rate = (c.tax_rate | default(0)) | float -%}
{%- set ns.rows = ns.rows + [{"key": c.tax_category ~ "|" ~ rate, "category": c.tax_category, "rate": rate, "net": c.amount | float}] -%}
{%- set ns.charge_total = ns.charge_total + (c.amount | float) -%}
{%- endfor -%}
{%- if computed -%}
{%- set breakdown = computed.vat_breakdown -%}
{%- set totals = computed.totals -%}
{%- else -%}
{%- set bns = namespace(items=[], tax=0.0) -%}
{%- for group in ns.rows | groupby("key") -%}
{%- set first = group.list | first -%}
{%- set basis = (group.list | sum_by("net")) | round(2) -%}
{%- set tax = (basis * first.rate / 100) | round(2) -%}
{%- set bns.items = bns.items + [{"category": first.category, "rate": first.rate, "basis": basis, "tax": tax}] -%}
{%- set bns.tax = bns.tax + tax -%}
{%- endfor -%}
{%- set tax_basis = (ns.line_total - ns.allowance_total + ns.charge_total) | round(2) -%}
{%- set grand = (tax_basis + bns.tax) | round(2) -%}
{%- set prepaid = (inv.prepaid_amount | default(0)) | float -%}
{%- set rounding = (inv.rounding_amount | default(0)) | float -%}
{%- set breakdown = bns.items -%}
{%- set totals = {"line_total": ns.line_total | round(2), "allowance_total": ns.allowance_total | round(2), "charge_total": ns.charge_total | round(2), "tax_basis_total": tax_basis, "tax_total": bns.tax | round(2), "grand_total": grand, "prepaid": prepaid, "rounding": rounding, "due_payable": (grand - prepaid + rounding) | round(2)} -%}
{%- endif -%}
{%- set several_rates = breakdown | length > 1 -%}
{%- macro money(value) -%}{{ value | format_currency(cur, loc) }}{%- endmacro -%}
{%- macro day(value) -%}{{ value | format_date("long", loc) }}{%- endmacro -%}
{%- macro rate_label(b) -%}
{%- if b.category in exemptions -%}{{ exemptions[b.category] }}{%- else -%}VAT {{ (b.rate | float) | format_number(loc) }}%{%- endif -%}
{%- endmacro -%}
{%- macro quantity(line) -%}
{%- set names = units[line.unit or "C62"] -%}
{{ line.quantity | format_number(loc) }} {% if names %}{{ names[0] if (line.quantity | float) == 1 else names[1] }}{% else %}{{ line.unit }}{% endif %}
{%- endmacro -%}
{%- macro address(party) -%}
{% if party.address.line1 %}{{ party.address.line1 }}<br>{% endif %}
{% if party.address.line2 %}{{ party.address.line2 }}<br>{% endif %}
{% if party.address.line3 %}{{ party.address.line3 }}<br>{% endif %}
{% if party.address.country in ["GB", "IE", "US"] %}{{ party.address.city }} {{ party.address.postcode }}{% else %}{{ party.address.postcode }} {{ party.address.city }}{% endif %}<br>
{{ countries[party.address.country] or party.address.country }}
{%- endmacro -%}
{%- macro grouped(value) -%}
{%- for i in range(0, value | length, 4) -%}{{ value[i:i + 4] }}{{ " " if not loop.last }}{%- endfor -%}
{%- endmacro -%}
<header class="masthead">
<div class="brand">
<svg viewBox="0 0 40 40" aria-hidden="true">
<rect x="2" y="2" width="36" height="36" rx="9" fill="#0e5c70"/>
<path d="M12 28V12h9.5a5 5 0 0 1 0 10H12m9.5 0H24a3 3 0 0 1 0 6H12" fill="none" stroke="#fff" stroke-width="3" stroke-linejoin="round"/>
</svg>
<div>
<div class="brand-name">{{ inv.seller.trading_name or inv.seller.name }}</div>
<div class="brand-meta">{% if website is defined and website %}{{ website }} · {% endif %}{% if inv.seller.contact and inv.seller.contact.email %}{{ inv.seller.contact.email }}{% endif %}</div>
</div>
</div>
<div class="doc-title">
<h1>{{ "Credit note" if inv.type_code in ["381", "261"] else "Invoice" }}</h1>
<div class="number">{{ inv.number }}</div>
</div>
</header>
<section class="parties">
<div>
<p class="label">From</p>
<p><strong>{{ inv.seller.name }}</strong></p>
<p class="muted">{{ address(inv.seller) }}</p>
{% if inv.seller.vat_id %}<p class="muted">VAT {{ inv.seller.vat_id }}</p>{% endif %}
{% if inv.seller.legal_registration %}<p class="muted">Company no. {{ inv.seller.legal_registration.id }}</p>{% endif %}
</div>
<div>
<p class="label">Bill to</p>
<p><strong>{{ inv.buyer.name }}</strong></p>
{% if inv.buyer.contact and inv.buyer.contact.name %}<p>Attn. {{ inv.buyer.contact.name }}</p>{% endif %}
<p class="muted">{{ address(inv.buyer) }}</p>
{% if inv.buyer.vat_id %}<p class="muted">VAT {{ inv.buyer.vat_id }}</p>{% endif %}
</div>
<div>
<p class="label">Details</p>
<table class="meta-table">
<tr><th scope="row">Issued</th><td>{{ day(inv.issue_date) }}</td></tr>
{% if inv.delivery and inv.delivery.date %}<tr><th scope="row">Supplied</th><td>{{ day(inv.delivery.date) }}</td></tr>{% endif %}
{% if inv.invoice_period %}<tr><th scope="row">Period</th><td>{{ inv.invoice_period.start | format_date("medium", loc) }} – {{ inv.invoice_period.end | format_date("medium", loc) }}</td></tr>{% endif %}
{% if inv.due_date %}<tr><th scope="row">Due</th><td><strong>{{ day(inv.due_date) }}</strong></td></tr>{% endif %}
{% if inv.payment_terms %}<tr><th scope="row">Terms</th><td>{{ inv.payment_terms }}</td></tr>{% endif %}
{% if inv.order_reference %}<tr><th scope="row">PO</th><td>{{ inv.order_reference }}</td></tr>{% endif %}
{% if inv.buyer_reference %}<tr><th scope="row">Your ref.</th><td>{{ inv.buyer_reference }}</td></tr>{% endif %}
</table>
</div>
</section>
<table class="items">
<thead>
<tr>
<th class="index" scope="col">#</th>
<th scope="col">Description</th>
<th class="num" scope="col">Qty</th>
<th class="num" scope="col">Unit price</th>
{% if several_rates %}<th class="num" scope="col">VAT</th>{% endif %}
<th class="num" scope="col">Amount</th>
</tr>
</thead>
<tbody>
{% for line in inv.lines %}
<tr>
<td class="index">{{ line.id or loop.index }}</td>
<td>
{{ line.item.name }}
{% if line.item.description %}<span class="details">{{ line.item.description }}</span>{% endif %}
{% for a in line.allowances | default([]) %}<span class="details">Less {{ a.reason or "discount" }}: {{ money(a.amount | float) }}</span>{% endfor %}
{% for c in line.charges | default([]) %}<span class="details">Plus {{ c.reason or "charge" }}: {{ money(c.amount | float) }}</span>{% endfor %}
</td>
<td class="num">{{ quantity(line) }}</td>
<td class="num">{{ money(line.price.net) }}</td>
{% if several_rates %}<td class="num">{% if line.tax.category in exemptions %}{{ line.tax.category }}{% else %}{{ (line.tax.rate | float) | format_number(loc) }}%{% endif %}</td>{% endif %}
<td class="num">{{ money(ns.nets[loop.index0]) }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<section class="summary">
<div class="notes">
{% for note in inv.notes | default([]) %}<p>{{ note.text }}</p>{% endfor %}
{% for b in breakdown %}{% if b.category in exemptions and inv.vat_exemptions and inv.vat_exemptions[b.category] and inv.vat_exemptions[b.category].reason %}<p>{{ exemptions[b.category] }}: {{ inv.vat_exemptions[b.category].reason }}</p>{% endif %}{% endfor %}
{% if embedded %}<p class="einvoice-hint">This PDF carries an embedded e-invoice ({{ profiles[embedded.profile] or embedded.profile }} XML).</p>{% endif %}
</div>
<table class="totals">
<tr><th scope="row">Subtotal</th><td class="num">{{ money(totals.line_total) }}</td></tr>
{% if (totals.allowance_total | float) != 0 %}<tr><th scope="row">Discounts</th><td class="num">−{{ money(totals.allowance_total) }}</td></tr>{% endif %}
{% if (totals.charge_total | float) != 0 %}<tr><th scope="row">Charges</th><td class="num">{{ money(totals.charge_total) }}</td></tr>{% endif %}
{% if (totals.allowance_total | float) != 0 or (totals.charge_total | float) != 0 %}<tr><th scope="row">Net total</th><td class="num">{{ money(totals.tax_basis_total) }}</td></tr>{% endif %}
{% for b in breakdown %}
<tr><th scope="row">{{ rate_label(b) }}{% if several_rates %} on {{ money(b.basis) }}{% endif %}</th><td class="num">{{ money(b.tax) }}</td></tr>
{% endfor %}
<tr class="grand"><th scope="row">Total</th><td class="num">{{ money(totals.grand_total) }}</td></tr>
{% if (totals.prepaid | float) != 0 %}<tr><th scope="row">Paid</th><td class="num">−{{ money(totals.prepaid) }}</td></tr>{% endif %}
{% if (totals.rounding | float) != 0 %}<tr><th scope="row">Rounding</th><td class="num">{{ money(totals.rounding) }}</td></tr>{% endif %}
<tr class="due"><th scope="row">Balance due</th><td class="num">{{ money(totals.due_payable) }}</td></tr>
</table>
</section>
{% if inv.payment %}
<section class="payment">
<div>
<p class="label">Payment details</p>
<dl>
{% if bank_name is defined and bank_name %}<dt>Bank</dt><dd>{{ bank_name }}</dd>{% endif %}
{% for account in inv.payment.credit_transfers | default([]) %}
<dt>Account name</dt><dd>{{ account.account_name or inv.seller.name }}</dd>
<dt>{{ "IBAN" if (account.account_id | length) >= 15 else "Account" }}</dt><dd>{{ grouped(account.account_id) }}</dd>
{% if account.bic %}<dt>BIC</dt><dd>{{ account.bic }}</dd>{% endif %}
{% endfor %}
<dt>Reference</dt><dd>{{ inv.payment.remittance_information or inv.number }}</dd>
</dl>
</div>
{% if payment_link is defined and payment_link %}
<div class="qr">
{{ payment_link | qrcode(size=240, margin=0, ecc="M", alt="QR code linking to the online payment page") }}
Scan to pay online
</div>
{% endif %}
</section>
{% endif %}
<title>{{ "Credit note" if invoice.type_code in ["381", "261"] else "Invoice" }} {{ invoice.number }} – {{ invoice.seller.trading_name or invoice.seller.name }}</title>
<style>
:root {
--ink: #1d2330;
--muted: #5d6675;
--rule: #d8dde4;
--soft: #f3f5f8;
--accent: #0e5c70;
}
* { box-sizing: border-box; }
html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body {
margin: 0;
color: var(--ink);
font-family: "Inter", "Liberation Sans", Arial, sans-serif;
font-size: 9.5pt;
line-height: 1.45;
}
.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.muted { color: var(--muted); }
.label { font-size: 7.5pt; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); margin: 0 0 1.5mm; }
.masthead { display: flex; justify-content: space-between; align-items: flex-start; padding-bottom: 7mm; border-bottom: 0.4mm solid var(--ink); }
.brand { display: flex; gap: 3mm; align-items: center; }
.brand svg { width: 11mm; height: 11mm; }
.brand-name { font-size: 11pt; font-weight: 700; letter-spacing: -0.01em; }
.brand-meta { font-size: 8pt; color: var(--muted); }
.doc-title { text-align: right; }
.doc-title h1 { margin: 0; font-size: 26pt; font-weight: 600; letter-spacing: -0.02em; line-height: 1; }
.doc-title .number { margin-top: 2mm; font-size: 10pt; color: var(--accent); font-weight: 600; }
.parties { display: grid; grid-template-columns: 1fr 1fr 0.9fr; gap: 8mm; margin: 7mm 0 8mm; }
.parties p { margin: 0; }
.meta-table { width: 100%; border-collapse: collapse; }
.meta-table td, .meta-table th { padding: 0.6mm 0; }
.meta-table th { color: var(--muted); padding-right: 3mm; font-weight: inherit; text-align: inherit; }
table.items { width: 100%; border-collapse: collapse; }
table.items thead { display: table-header-group; }
table.items th {
font-size: 7.5pt; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted);
text-align: left; padding: 2.2mm 2mm; border-bottom: 0.3mm solid var(--ink);
}
table.items td { padding: 2.6mm 2mm; border-bottom: 0.2mm solid var(--rule); vertical-align: top; }
table.items tr { break-inside: avoid; page-break-inside: avoid; }
table.items .index { width: 7mm; color: var(--muted); }
table.items .details { display: block; font-size: 8pt; color: var(--muted); margin-top: 0.6mm; }
.summary { display: grid; grid-template-columns: 1fr 72mm; gap: 10mm; margin-top: 7mm; break-inside: avoid; page-break-inside: avoid; }
.totals { width: 100%; border-collapse: collapse; }
.totals td, .totals th { padding: 1.6mm 2mm; }
.totals th { font-weight: inherit; text-align: inherit; }
.totals .grand td, .totals .grand th { border-top: 0.4mm solid var(--ink); font-size: 12pt; font-weight: 700; padding-top: 3mm; }
.totals .due td, .totals .due th { background: var(--soft); color: var(--accent); font-weight: 700; }
.payment { display: grid; grid-template-columns: 1fr auto; gap: 6mm; align-items: center; margin-top: 8mm; padding: 5mm; border: 0.3mm solid var(--rule); border-radius: 2mm; break-inside: avoid; page-break-inside: avoid; }
.payment dl { display: grid; grid-template-columns: auto 1fr; gap: 1mm 4mm; margin: 0; }
.payment dt { color: var(--muted); }
.payment dd { margin: 0; font-variant-numeric: tabular-nums; }
.qr { text-align: center; font-size: 7pt; color: var(--muted); }
.qr img { display: block; width: 26mm; height: 26mm; margin: 0 auto 1mm; }
.notes { margin-top: 8mm; font-size: 8.5pt; color: var(--muted); max-width: 140mm; }
.notes p { margin: 0 0 2mm; }
.notes strong { color: var(--ink); }
.notes .einvoice-hint { margin-top: 4mm; font-size: 7.5pt; }
</style>Render it with the API
After adding the template to your workspace, render it by ID and pass your data. Arrays are shortened here.
curl -X POST https://api-eu.dynamicdocumentapi.com/v1/renders \
-H "Authorization: Bearer $DYNAMIC_DOCUMENT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"input": {
"type": "template",
"template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C"
},
"data": {
"invoice": {
"number": "BF-2026-0147",
"issue_date": "2026-09-14",
"due_date": "2026-10-14",
"currency": "GBP",
"buyer_reference": "ORR-MKT-2291",
"order_reference": "PO-44812",
"payment_terms": "30 days net",
"delivery": {
"date": "2026-09-11"
},
"seller": {
"name": "Brambleworth & Finch Design Studio",
"vat_id": "GB123456789",
"electronic_address": {
"id": "accounts@brambleworth.example.com",
"scheme": "EM"
},
"address": {
"line1": "Unit 4, Tannery Yard",
"line2": "14 Wharf Lane",
"city": "Bristol",
"postcode": "BS1 6XY",
"country": "GB"
},
"contact": {
"name": "Accounts team",
"phone": "+44 117 496 0321",
"email": "accounts@brambleworth.example.com"
}
},
"buyer": {
"name": "Orrery Instruments Ltd",
"vat_id": "GB987654321",
"electronic_address": {
"id": "invoices@orrery-instruments.example.com",
"scheme": "EM"
},
"address": {
"line1": "Floor 2, Mercator House",
"line2": "27 Alderney Street",
"city": "Leeds",
"postcode": "LS2 8QR",
"country": "GB"
},
"contact": {
"name": "Priya Castellane"
}
},
"payment": {
"means_code": "30",
"remittance_information": "BF-2026-0147",
"credit_transfers": [
{
"account_id": "GB52EXMP12345612345678",
"account_name": "Brambleworth & Finch Design Studio",
"bic": "EXMPGB2L"
}
]
},
"lines": [
{
"quantity": "2",
"unit": "DAY",
"price": {
"net": "850.00"
},
"tax": {
"category": "S",
"rate": "20"
},
"item": {
"name": "Brand identity refresh — discovery workshops",
"description": "Two half-day workshops and six stakeholder interviews"
}
},
{
"quantity": "1",
"unit": "LS",
"price": {
"net": "2400.00"
},
"tax": {
"category": "S",
"rate": "20"
},
"item": {
"name": "Logo system and usage guidelines",
"description": "Primary and secondary marks, 24-page guideline document"
}
}
],
"notes": [
{
"text": "Thank you for working with us on the autumn range."
},
{
"text": "Questions about this invoice? Reply to accounts@brambleworth.example.com."
}
]
},
"website": "brambleworth.example.com",
"bank_name": "Westcombe Mutual Bank",
"payment_link": "https://pay.example.com/i/BF-2026-0147"
},
"output": {
"format": "pdf",
"filename": "invoice-standard.pdf"
}
}'const response = await fetch("https://api-eu.dynamicdocumentapi.com/v1/renders", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.DYNAMIC_DOCUMENT_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
"input": {
"type": "template",
"template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C"
},
"data": {
"invoice": {
"number": "BF-2026-0147",
"issue_date": "2026-09-14",
"due_date": "2026-10-14",
"currency": "GBP",
"buyer_reference": "ORR-MKT-2291",
"order_reference": "PO-44812",
"payment_terms": "30 days net",
"delivery": {
"date": "2026-09-11"
},
"seller": {
"name": "Brambleworth & Finch Design Studio",
"vat_id": "GB123456789",
"electronic_address": {
"id": "accounts@brambleworth.example.com",
"scheme": "EM"
},
"address": {
"line1": "Unit 4, Tannery Yard",
"line2": "14 Wharf Lane",
"city": "Bristol",
"postcode": "BS1 6XY",
"country": "GB"
},
"contact": {
"name": "Accounts team",
"phone": "+44 117 496 0321",
"email": "accounts@brambleworth.example.com"
}
},
"buyer": {
"name": "Orrery Instruments Ltd",
"vat_id": "GB987654321",
"electronic_address": {
"id": "invoices@orrery-instruments.example.com",
"scheme": "EM"
},
"address": {
"line1": "Floor 2, Mercator House",
"line2": "27 Alderney Street",
"city": "Leeds",
"postcode": "LS2 8QR",
"country": "GB"
},
"contact": {
"name": "Priya Castellane"
}
},
"payment": {
"means_code": "30",
"remittance_information": "BF-2026-0147",
"credit_transfers": [
{
"account_id": "GB52EXMP12345612345678",
"account_name": "Brambleworth & Finch Design Studio",
"bic": "EXMPGB2L"
}
]
},
"lines": [
{
"quantity": "2",
"unit": "DAY",
"price": {
"net": "850.00"
},
"tax": {
"category": "S",
"rate": "20"
},
"item": {
"name": "Brand identity refresh — discovery workshops",
"description": "Two half-day workshops and six stakeholder interviews"
}
},
{
"quantity": "1",
"unit": "LS",
"price": {
"net": "2400.00"
},
"tax": {
"category": "S",
"rate": "20"
},
"item": {
"name": "Logo system and usage guidelines",
"description": "Primary and secondary marks, 24-page guideline document"
}
}
],
"notes": [
{
"text": "Thank you for working with us on the autumn range."
},
{
"text": "Questions about this invoice? Reply to accounts@brambleworth.example.com."
}
]
},
"website": "brambleworth.example.com",
"bank_name": "Westcombe Mutual Bank",
"payment_link": "https://pay.example.com/i/BF-2026-0147"
},
"output": {
"format": "pdf",
"filename": "invoice-standard.pdf"
}
}),
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
const result = await response.json();
console.log(result);import os
import uuid
import requests
response = requests.post(
"https://api-eu.dynamicdocumentapi.com/v1/renders",
headers={
"Authorization": f"Bearer {os.environ['DYNAMIC_DOCUMENT_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"input": {
"type": "template",
"template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C",
},
"data": {
"invoice": {
"number": "BF-2026-0147",
"issue_date": "2026-09-14",
"due_date": "2026-10-14",
"currency": "GBP",
"buyer_reference": "ORR-MKT-2291",
"order_reference": "PO-44812",
"payment_terms": "30 days net",
"delivery": {
"date": "2026-09-11",
},
"seller": {
"name": "Brambleworth & Finch Design Studio",
"vat_id": "GB123456789",
"electronic_address": {
"id": "accounts@brambleworth.example.com",
"scheme": "EM",
},
"address": {
"line1": "Unit 4, Tannery Yard",
"line2": "14 Wharf Lane",
"city": "Bristol",
"postcode": "BS1 6XY",
"country": "GB",
},
"contact": {
"name": "Accounts team",
"phone": "+44 117 496 0321",
"email": "accounts@brambleworth.example.com",
},
},
"buyer": {
"name": "Orrery Instruments Ltd",
"vat_id": "GB987654321",
"electronic_address": {
"id": "invoices@orrery-instruments.example.com",
"scheme": "EM",
},
"address": {
"line1": "Floor 2, Mercator House",
"line2": "27 Alderney Street",
"city": "Leeds",
"postcode": "LS2 8QR",
"country": "GB",
},
"contact": {
"name": "Priya Castellane",
},
},
"payment": {
"means_code": "30",
"remittance_information": "BF-2026-0147",
"credit_transfers": [
{
"account_id": "GB52EXMP12345612345678",
"account_name": "Brambleworth & Finch Design Studio",
"bic": "EXMPGB2L",
},
],
},
"lines": [
{
"quantity": "2",
"unit": "DAY",
"price": {
"net": "850.00",
},
"tax": {
"category": "S",
"rate": "20",
},
"item": {
"name": "Brand identity refresh — discovery workshops",
"description": "Two half-day workshops and six stakeholder interviews",
},
},
{
"quantity": "1",
"unit": "LS",
"price": {
"net": "2400.00",
},
"tax": {
"category": "S",
"rate": "20",
},
"item": {
"name": "Logo system and usage guidelines",
"description": "Primary and secondary marks, 24-page guideline document",
},
},
],
"notes": [
{
"text": "Thank you for working with us on the autumn range.",
},
{
"text": "Questions about this invoice? Reply to accounts@brambleworth.example.com.",
},
],
},
"website": "brambleworth.example.com",
"bank_name": "Westcombe Mutual Bank",
"payment_link": "https://pay.example.com/i/BF-2026-0147",
},
"output": {
"format": "pdf",
"filename": "invoice-standard.pdf",
},
},
timeout=90,
)
response.raise_for_status()
print(response.json())More invoices templates
- 210 × 297 mm
Bold brand invoice
A4A confident, colourful A4 invoice for studios and product companies: a brand-coloured hero with the amount due, brand colours you set from data, itemised EN 16931 lines, VAT, a pay-by-link QR code and the same data embedded as ZUGFeRD / Factur-X EN 16931 XML.
- 210 × 297 mm
E-Rechnung (ZUGFeRD / XRechnung)
A4A German A4 invoice rendered straight from the EN 16931 invoice model, so the same data produces the visible invoice and the embedded Factur-X / ZUGFeRD or XRechnung XML (output.einvoice.invoice_path = "invoice"). It prints the engine's computed amounts, lists VAT per rate, the Leitweg-ID and the bank details.
- 210 × 297 mm
EU reverse-charge invoice
A4An A4 invoice for cross-border B2B services within the EU, rendered from the EN 16931 invoice model: both VAT IDs, the date of supply, VAT category AE (reverse charge) with its exemption reason and notice, the total in words, bank transfer details and the same data embedded as ZUGFeRD / Factur-X EN 16931 XML.