CORE
Template examples
Three production-ready starting points — a clean invoice, a payment receipt and an account statement. Copy one, swap in your data, render.
How to use these
Each example is a self-contained HTML document: inline<style> only and no JavaScript (JS is disabled at render time, so everything is plain HTML + CSS). They're sized to print cleanly to A4 with sensible margins.
There are two ways to render one:
- As a stored template — paste the
*.htmltab into Dashboard → Templates, then render by itstpl_…id, passing the matchingdataobject each call (see Templates). - Inline — substitute your own values and POST the finished markup as the
htmlfield to /v1/render (see Rendering HTML).
About repeating rows. The template merge supports{{ value }}, raw {{{ value }}} and dotted paths — but no loops. So for line items or ledger rows you build the <tr> markup in your own code and drop it in with a raw {{{ lineItemsHtml }}} placeholder. The data.json tab shows the exact shape, and the invoice has a small Node helper for building the rows. (Escape any user-supplied text you put into a raw placeholder.)
Treat these as a starting point. The invoice is already brandable from data: its logo comes from{{ from.logoUrl }} (an https:// or data: image) and every accent colour reads a single--brand CSS variable fed by {{ brandColor }} — so one stored template renders for every client in their own look. See Make it your brand for logo, font and colour recipes.
Invoice
A clean, VAT-ready invoice: a logo header, from / bill-to blocks, a line-items table, a subtotal → VAT → total summary, and a payment-terms note. The logo and the accent colour are both driven from data — swap from.logoUrl and brandColor and the whole document re-themes.
<!doctype html>
<!-- One brand colour, fed from your data, drives every accent via var(--brand). -->
<html lang="en" style="--brand: {{brandColor}}">
<head>
<meta charset="utf-8" />
<style>
* { box-sizing: border-box; }
html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body {
margin: 0;
font-family: "Helvetica Neue", Arial, sans-serif;
color: #1a1a1a;
font-size: 13px;
line-height: 1.5;
}
.sheet { padding: 48px 56px; }
.top {
display: flex;
justify-content: space-between;
align-items: flex-start;
border-bottom: 2px solid var(--brand);
padding-bottom: 24px;
}
.brand img { height: 40px; display: block; }
.brand small { display: block; font-weight: 400; font-size: 12px; color: #6b7280; margin-top: 6px; }
.doc-meta { text-align: right; }
.doc-meta h1 { margin: 0; font-size: 26px; letter-spacing: 0.04em; text-transform: uppercase; color: var(--brand); }
.doc-meta .num { font-size: 14px; font-weight: 600; margin-top: 4px; }
.doc-meta .dates { font-size: 12px; color: #6b7280; margin-top: 6px; }
.parties { display: flex; gap: 48px; margin-top: 32px; }
.parties .label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: #9ca3af; margin-bottom: 6px; }
.parties strong { display: block; font-size: 14px; }
.parties .addr { color: #4b5563; white-space: pre-line; }
table { width: 100%; border-collapse: collapse; margin-top: 36px; }
thead th {
text-align: left;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #6b7280;
border-bottom: 1px solid #e5e7eb;
padding: 0 8px 8px;
}
thead th.r { text-align: right; }
tbody td { padding: 12px 8px; border-bottom: 1px solid #f0f1f3; vertical-align: top; }
tbody td.r { text-align: right; }
tbody td.desc { font-weight: 500; }
tr { break-inside: avoid; }
.totals { width: 280px; margin-left: auto; margin-top: 20px; }
.totals .row { display: flex; justify-content: space-between; padding: 6px 8px; color: #4b5563; }
.totals .grand {
border-top: 2px solid var(--brand);
margin-top: 6px;
padding-top: 12px;
font-size: 17px;
font-weight: 700;
color: var(--brand);
}
.notes { margin-top: 40px; font-size: 12px; color: #4b5563; }
.notes .label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: #9ca3af; margin-bottom: 4px; }
footer { margin-top: 48px; border-top: 1px solid #e5e7eb; padding-top: 14px; font-size: 11px; color: #9ca3af; text-align: center; }
</style>
</head>
<body>
<div class="sheet">
<div class="top">
<div class="brand">
<!-- Your logo, fed from data (https:// URL or data: base64). -->
<img src="{{from.logoUrl}}" alt="{{from.name}}" />
<small>{{from.tagline}}</small>
</div>
<div class="doc-meta">
<h1>Invoice</h1>
<div class="num">{{number}}</div>
<div class="dates">
Issued {{issued}}<br />
Due {{due}}
</div>
</div>
</div>
<div class="parties">
<div>
<div class="label">From</div>
<strong>{{from.name}}</strong>
<div class="addr">{{from.address}}</div>
<div class="addr">VAT {{from.vat}}</div>
</div>
<div>
<div class="label">Bill to</div>
<strong>{{billTo.name}}</strong>
<div class="addr">{{billTo.address}}</div>
<div class="addr">{{billTo.email}}</div>
</div>
</div>
<table>
<thead>
<tr>
<th>Description</th>
<th class="r">Qty</th>
<th class="r">Unit</th>
<th class="r">Amount</th>
</tr>
</thead>
<!-- rows are built by your code (see data.json) and dropped in raw -->
<tbody>{{{ lineItemsHtml }}}</tbody>
</table>
<div class="totals">
<div class="row"><span>Subtotal</span><span>{{subtotal}}</span></div>
<div class="row"><span>VAT ({{vatRate}})</span><span>{{vat}}</span></div>
<div class="row grand"><span>Total due</span><span>{{total}}</span></div>
</div>
<div class="notes">
<div class="label">Payment</div>
{{paymentTerms}}
</div>
<footer>{{from.name}} · {{from.email}} · {{from.website}}</footer>
</div>
</body>
</html>Receipt
A compact, narrow payment receipt with a Paid badge, the charged amount, the card used and an itemised breakdown — ideal for post-payment confirmation emails.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<style>
* { box-sizing: border-box; }
html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body {
margin: 0;
font-family: "Helvetica Neue", Arial, sans-serif;
color: #1a1a1a;
font-size: 13px;
}
.receipt { max-width: 420px; margin: 0 auto; padding: 40px 32px; }
.logo { height: 32px; margin-bottom: 20px; }
.paid {
display: inline-block;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #047857;
background: #ecfdf5;
border: 1px solid #a7f3d0;
border-radius: 999px;
padding: 4px 12px;
}
h1 { font-size: 18px; margin: 16px 0 2px; }
.sub { color: #6b7280; font-size: 12px; }
.amount { font-size: 34px; font-weight: 700; letter-spacing: -0.02em; margin: 24px 0 4px; }
.meta { margin-top: 28px; border-top: 1px solid #e5e7eb; padding-top: 16px; }
.meta .row { display: flex; justify-content: space-between; padding: 5px 0; }
.meta .row span:first-child { color: #6b7280; }
.meta .row span:last-child { font-weight: 500; }
.items { margin-top: 20px; }
.items .line { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid #f0f1f3; }
.items .line .name small { display: block; color: #9ca3af; font-size: 11px; }
.total { display: flex; justify-content: space-between; padding: 14px 0 0; font-size: 15px; font-weight: 700; }
footer { margin-top: 32px; text-align: center; color: #9ca3af; font-size: 11px; line-height: 1.6; }
</style>
</head>
<body>
<div class="receipt">
<img class="logo" src="{{merchant.logoUrl}}" alt="{{merchant.name}}" />
<span class="paid">Paid</span>
<h1>Receipt from {{merchant.name}}</h1>
<div class="sub">{{receiptNumber}} · {{paidAt}}</div>
<div class="amount">{{total}}</div>
<div class="sub">Charged to {{payment.brand}} ending {{payment.last4}}</div>
<div class="items">
{{{ lineItemsHtml }}}
<div class="total"><span>Total</span><span>{{total}}</span></div>
</div>
<div class="meta">
<div class="row"><span>Billed to</span><span>{{customer.name}}</span></div>
<div class="row"><span>Email</span><span>{{customer.email}}</span></div>
<div class="row"><span>Payment method</span><span>{{payment.brand}} •••• {{payment.last4}}</span></div>
<div class="row"><span>Date</span><span>{{paidAt}}</span></div>
</div>
<footer>
{{merchant.name}} · {{merchant.address}}<br />
Questions? {{merchant.email}}
</footer>
</div>
</body>
</html>Statement
An account statement: a dated ledger of charges and credits over a period with an opening balance, totals and a closing balance due.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<style>
* { box-sizing: border-box; }
html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body { margin: 0; font-family: "Helvetica Neue", Arial, sans-serif; color: #1a1a1a; font-size: 13px; }
.sheet { padding: 48px 56px; }
header { display: flex; justify-content: space-between; align-items: flex-end; border-bottom: 2px solid #1a1a1a; padding-bottom: 20px; }
header .brand { font-size: 18px; font-weight: 700; }
header h1 { margin: 0; font-size: 22px; text-transform: uppercase; letter-spacing: 0.04em; color: #6b7280; }
.for { margin-top: 24px; color: #4b5563; }
.for strong { color: #1a1a1a; }
.period { color: #9ca3af; font-size: 12px; margin-top: 2px; }
table { width: 100%; border-collapse: collapse; margin-top: 28px; }
thead th { text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em; color: #6b7280; border-bottom: 1px solid #e5e7eb; padding: 0 8px 8px; }
thead th.r { text-align: right; }
tbody td { padding: 10px 8px; border-bottom: 1px solid #f0f1f3; }
tbody td.r { text-align: right; }
tr { break-inside: avoid; }
.summary { width: 300px; margin-left: auto; margin-top: 20px; }
.summary .row { display: flex; justify-content: space-between; padding: 6px 8px; color: #4b5563; }
.summary .balance { border-top: 2px solid #1a1a1a; margin-top: 6px; padding-top: 12px; font-size: 16px; font-weight: 700; color: #1a1a1a; }
footer { margin-top: 44px; border-top: 1px solid #e5e7eb; padding-top: 14px; font-size: 11px; color: #9ca3af; text-align: center; }
</style>
</head>
<body>
<div class="sheet">
<header>
<div class="brand">{{company.name}}</div>
<h1>Statement</h1>
</header>
<div class="for">
<strong>{{customer.name}}</strong> · Account {{customer.account}}
<div class="period">Statement period {{period}}</div>
</div>
<table>
<thead>
<tr>
<th>Date</th>
<th>Description</th>
<th class="r">Charges</th>
<th class="r">Credits</th>
</tr>
</thead>
<tbody>{{{ entriesHtml }}}</tbody>
</table>
<div class="summary">
<div class="row"><span>Opening balance</span><span>{{opening}}</span></div>
<div class="row"><span>Total charges</span><span>{{totalCharges}}</span></div>
<div class="row"><span>Total credits</span><span>{{totalCredits}}</span></div>
<div class="row balance"><span>Balance due</span><span>{{balance}}</span></div>
</div>
<footer>{{company.name}} · {{company.email}} · Generated {{generatedAt}}</footer>
</div>
</body>
</html>