80 × 220 mm
Receipts
Thermal POS receipt (80 mm)
A narrow 80 mm point-of-sale receipt for a café and bookshop, printed in black only. Items carry modifiers, the GST included in the total is derived from the lines, and the transaction ID is encoded as a Code 128 barcode.
Opens Dynamic Document API with this template preloaded in a free workspace.
Template language features
- Loops
- Conditionals
- Macros
- Filters
- Barcode
- Currency formatting
- Date formatting
- Number formatting
Filters such as format_currency and qrcode are described in the template language reference.
Variables
Derived from the sample data: 6 top-level fields, 35 paths in total. Send the same shape as data.
| Path | Type | Example |
|---|---|---|
| store | object | |
| store.name | string | "Wattle & Quill" |
| store.tagline | string | "Books · Coffee · Paper goods" |
| store.address | string | "Shop 3, 41 Jacaranda Arcade ⏎ Paddington QLD 4…" |
| store.abn | string | "12 345 678 901" |
| store.website | string | "wattleandquill.example.com" |
| sale | object | |
| sale.transaction_id | string | "WQ2-260917-0142" |
| sale.datetime | date | "2026-09-17T08:42:00+10:00" |
| sale.timezone | string | "Australia/Brisbane" |
| sale.register | string | "Till 2" |
| sale.served_by | string | "Jasmin" |
| sale.order_type | string | "Eat in · Table 6" |
| sale.currency | string | "AUD" |
| sale.locale | string | "en-AU" |
| sale.gst_rate | number | 0.1 |
| items | array<object> · 7 | |
| items[].name | string | "Flat white" |
| items[].variant | string | "Regular" |
| items[].quantity | number | 2 |
| items[].unit_price | number | 5.2 |
| items[].modifiers | array<object> · 1 | |
| items[].modifiers[].name | string | "Oat milk" |
| items[].modifiers[].price | number | 0.8 |
| items[].amount | number | 12 |
| payment | object | |
| payment.method | string | "EFTPOS" |
| payment.entry_mode | string | "Contactless" |
| payment.card | string | "Debit •••• 4821" |
| payment.auth_code | string | "063914" |
| loyalty | object | |
| loyalty.program | string | "Coffee card" |
| loyalty.stamps | number | 7 |
| loyalty.card_size | number | 10 |
| footer_lines | array<string> · 2 | "Books can be exchanged within 14 days with thi…" |
Sample data
Fictional records used for previews. Replace them with your own data when you call the API.
{
"store": {
"name": "Wattle & Quill",
"tagline": "Books · Coffee · Paper goods",
"address": "Shop 3, 41 Jacaranda Arcade\nPaddington QLD 4064",
"abn": "12 345 678 901",
"website": "wattleandquill.example.com"
},
"sale": {
"transaction_id": "WQ2-260917-0142",
"datetime": "2026-09-17T08:42:00+10:00",
"timezone": "Australia/Brisbane",
"register": "Till 2",
"served_by": "Jasmin",
"order_type": "Eat in · Table 6",
"currency": "AUD",
"locale": "en-AU",
"gst_rate": 0.1
},
"items": [
{
"name": "Flat white",
"variant": "Regular",
"quantity": 2,
"unit_price": 5.2,
"modifiers": [{ "name": "Oat milk", "price": 0.8 }],
"amount": 12.0
},
{
"name": "Chai latte",
"variant": "Large",
"quantity": 1,
"unit_price": 6.2,
"modifiers": [
{ "name": "Soy milk", "price": 0.8 },
{ "name": "Extra hot", "price": 0 }
],
"amount": 7.0
},
{
"name": "Banana bread",
"variant": null,
"quantity": 1,
"unit_price": 6.5,
"modifiers": [
{ "name": "Toasted", "price": 0 },
{ "name": "Whipped butter", "price": 1.0 }
],
"amount": 7.5
},
{
"name": "The Cartographer's Garden",
"variant": null,
"quantity": 1,
"unit_price": 32.99,
"modifiers": [],
"amount": 32.99
},
{
"name": "Field Notes on Small Birds",
"variant": null,
"quantity": 1,
"unit_price": 24.95,
"modifiers": [],
"amount": 24.95
},
{
"name": "Pressed-flower bookmark",
"variant": null,
"quantity": 2,
"unit_price": 4.5,
"modifiers": [],
"amount": 9.0
},
{
"name": "Letterpress greeting card",
"variant": null,
"quantity": 1,
"unit_price": 6.0,
"modifiers": [],
"amount": 6.0
}
],
"payment": {
"method": "EFTPOS",
"entry_mode": "Contactless",
"card": "Debit •••• 4821",
"auth_code": "063914"
},
"loyalty": {
"program": "Coffee card",
"stamps": 7,
"card_size": 10
},
"footer_lines": [
"Books can be exchanged within 14 days with this receipt.",
"Poetry night: first Thursday of the month, 6.30 pm."
]
}Source
The body markup with Jinja, the stylesheet placed in <head>.
{#- 80 mm point-of-sale receipt. Line amounts include modifiers; the total, item count and the
GST contained in GST-inclusive prices are derived from the items. Black only, for thermal printers. -#}
{%- set locale = sale.locale | default("en-AU") -%}
{%- set currency = sale.currency -%}
{%- set total = items | sum_by("amount") -%}
{%- set gst = (total * sale.gst_rate / (1 + sale.gst_rate)) | round(2) -%}
{%- set item_count = items | sum_by("quantity") | int -%}
{%- macro figure(value) -%}{{ value | format_number(locale, 2) }}{%- endmacro -%}
<div class="receipt">
<header class="shop">
<svg class="logo" viewBox="0 0 48 30" aria-hidden="true">
<path d="M5 27C13 20 25 8 42 3c-5 9-15 19-29 22z" fill="#000000"/>
<path d="M8 25.5L37 6.5" stroke="#ffffff" stroke-width="0.9"/>
<circle cx="35" cy="20" r="2.6" fill="#000000"/>
<circle cx="40.5" cy="17" r="2.2" fill="#000000"/>
<circle cx="40" cy="23.5" r="2.4" fill="#000000"/>
<circle cx="45" cy="21" r="1.8" fill="#000000"/>
</svg>
<div class="shop-name">{{ store.name }}</div>
<div class="shop-tagline">{{ store.tagline }}</div>
<div class="shop-address">{{ store.address | nl2br }}</div>
<div>ABN {{ store.abn }}</div>
</header>
<h1 class="doc-title">Tax invoice</h1>
<div class="meta">
<div class="row">
<span>{{ sale.datetime | format_date("%d/%m/%Y %H:%M", locale, sale.timezone) }}</span>
<span>{{ sale.register }}</span>
</div>
<div class="row"><span>Receipt</span><span>{{ sale.transaction_id }}</span></div>
<div class="row"><span>Served by {{ sale.served_by }}</span><span>{{ sale.order_type }}</span></div>
</div>
<div class="rule"></div>
<div class="items">
{% for item in items %}
{%- set each = item.unit_price + (item.modifiers | sum_by("price")) -%}
<div class="item">
<div class="row">
<span class="qty">{{ item.quantity }}×</span>
<span class="name">{{ item.name }}{% if item.variant %} · {{ item.variant }}{% endif %}</span>
<span class="amt">{{ figure(item.amount) }}</span>
</div>
{% for modifier in item.modifiers %}
<div class="row sub">
<span class="qty"></span>
<span class="name">+ {{ modifier.name }}{% if modifier.price %} ({{ figure(modifier.price) }}){% endif %}</span>
<span class="amt"></span>
</div>
{% endfor %}
{% if item.quantity > 1 %}
<div class="row sub">
<span class="qty"></span>
<span class="name">{{ item.quantity }} @ {{ figure(each) }} each</span>
<span class="amt"></span>
</div>
{% endif %}
</div>
{% endfor %}
</div>
<div class="rule"></div>
<div class="totals">
<div class="row"><span>{{ item_count }} item{{ "s" if item_count != 1 else "" }}</span><span></span></div>
<div class="row grand"><span>Total</span><span>{{ total | format_currency(currency, locale) }}</span></div>
<div class="row"><span>Includes GST</span><span>{{ gst | format_currency(currency, locale) }}</span></div>
</div>
<div class="rule"></div>
<div class="payment">
<div class="row">
<span>{{ payment.method }}{% if payment.entry_mode %} · {{ payment.entry_mode }}{% endif %}</span>
<span>{{ total | format_currency(currency, locale) }}</span>
</div>
<div class="row sub">
<span>{{ payment.card }}</span>
{% if payment.auth_code %}<span>Auth {{ payment.auth_code }}</span>{% endif %}
</div>
</div>
{% if loyalty %}
{%- set remaining = loyalty.card_size - loyalty.stamps -%}
<div class="rule"></div>
<div class="loyalty">
<div class="loyalty-title">{{ loyalty.program }}</div>
<div class="stamps">
{% for i in range(1, loyalty.card_size + 1) %}<span class="stamp{% if i <= loyalty.stamps %} filled{% endif %}"></span>{% endfor %}
</div>
<div class="loyalty-note">
{% if remaining > 0 %}{{ remaining }} more stamp{{ "s" if remaining != 1 else "" }} for a free coffee{% else %}Your next coffee is on us{% endif %}
</div>
</div>
{% endif %}
<div class="barcode">{{ sale.transaction_id | barcode("code128", alt="Barcode " ~ sale.transaction_id) }}</div>
<footer class="foot">
{% for line in footer_lines %}<p>{{ line }}</p>{% endfor %}
<p class="thanks">Thank you for visiting</p>
<p>{{ store.website }}</p>
</footer>
</div><title>Tax invoice {{ sale.transaction_id }} – {{ store.name }}</title>
<style>
* { box-sizing: border-box; }
html { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body {
margin: 0;
color: #000000;
font-family: "DejaVu Sans Mono", "Liberation Mono", "Courier New", monospace;
font-size: 7.5pt;
line-height: 1.3;
}
p { margin: 0; }
.receipt { width: 100%; }
.shop { text-align: center; }
.logo { display: block; width: 14mm; height: 9mm; margin: 0 auto 1mm; }
.shop-name {
font-family: "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
font-size: 12pt; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; line-height: 1.2;
}
.shop-tagline { margin-bottom: 1mm; }
.doc-title {
margin: 2.5mm 0 2mm; padding: 1mm 0; border-top: 0.5mm solid #000000; border-bottom: 0.5mm solid #000000;
text-align: center; font-weight: 700; font-size: 9pt; letter-spacing: 0.3em; text-transform: uppercase;
}
.row { display: flex; justify-content: space-between; gap: 2mm; }
.row > span:last-child { text-align: right; white-space: nowrap; }
.rule { margin: 1.8mm 0; border-top: 0.3mm dashed #000000; }
.items .item { margin-bottom: 0.9mm; }
.items .item:last-child { margin-bottom: 0; }
.items .qty { flex: none; width: 6mm; }
.items .name { flex: 1; }
.items .amt { flex: none; width: 13mm; }
.items .sub { font-size: 7pt; }
.totals .grand {
margin: 0.8mm 0; padding: 0.8mm 0; border-top: 0.3mm solid #000000; border-bottom: 0.3mm solid #000000;
font-size: 10.5pt; font-weight: 700; text-transform: uppercase;
}
.payment .sub { font-size: 7pt; }
.loyalty { text-align: center; }
.loyalty-title { font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; }
.stamps { display: flex; justify-content: center; gap: 1.6mm; margin: 1.2mm 0; }
.stamp { width: 3.6mm; height: 3.6mm; border: 0.35mm solid #000000; border-radius: 50%; }
.stamp.filled { background: #000000; }
.loyalty-note { font-size: 7pt; }
.barcode { margin: 3mm auto 1.5mm; text-align: center; }
.barcode img { display: block; width: 58mm; height: 13mm; margin: 0 auto; }
.foot { text-align: center; font-size: 7pt; }
.foot p + p { margin-top: 0.4mm; }
.foot .thanks {
margin: 1.5mm 0 0.5mm; font-family: "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
font-size: 9pt; font-weight: 700;
}
</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": {
"store": {
"name": "Wattle & Quill",
"tagline": "Books · Coffee · Paper goods",
"address": "Shop 3, 41 Jacaranda Arcade\nPaddington QLD 4064",
"abn": "12 345 678 901",
"website": "wattleandquill.example.com"
},
"sale": {
"transaction_id": "WQ2-260917-0142",
"datetime": "2026-09-17T08:42:00+10:00",
"timezone": "Australia/Brisbane",
"register": "Till 2",
"served_by": "Jasmin",
"order_type": "Eat in · Table 6",
"currency": "AUD",
"locale": "en-AU",
"gst_rate": 0.1
},
"items": [
{
"name": "Flat white",
"variant": "Regular",
"quantity": 2,
"unit_price": 5.2,
"modifiers": [
{
"name": "Oat milk",
"price": 0.8
}
],
"amount": 12
},
{
"name": "Chai latte",
"variant": "Large",
"quantity": 1,
"unit_price": 6.2,
"modifiers": [
{
"name": "Soy milk",
"price": 0.8
},
{
"name": "Extra hot",
"price": 0
}
],
"amount": 7
}
],
"payment": {
"method": "EFTPOS",
"entry_mode": "Contactless",
"card": "Debit •••• 4821",
"auth_code": "063914"
},
"loyalty": {
"program": "Coffee card",
"stamps": 7,
"card_size": 10
},
"footer_lines": [
"Books can be exchanged within 14 days with this receipt.",
"Poetry night: first Thursday of the month, 6.30 pm."
]
},
"output": {
"format": "pdf",
"filename": "receipt-thermal-80mm.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": {
"store": {
"name": "Wattle & Quill",
"tagline": "Books · Coffee · Paper goods",
"address": "Shop 3, 41 Jacaranda Arcade\nPaddington QLD 4064",
"abn": "12 345 678 901",
"website": "wattleandquill.example.com"
},
"sale": {
"transaction_id": "WQ2-260917-0142",
"datetime": "2026-09-17T08:42:00+10:00",
"timezone": "Australia/Brisbane",
"register": "Till 2",
"served_by": "Jasmin",
"order_type": "Eat in · Table 6",
"currency": "AUD",
"locale": "en-AU",
"gst_rate": 0.1
},
"items": [
{
"name": "Flat white",
"variant": "Regular",
"quantity": 2,
"unit_price": 5.2,
"modifiers": [
{
"name": "Oat milk",
"price": 0.8
}
],
"amount": 12
},
{
"name": "Chai latte",
"variant": "Large",
"quantity": 1,
"unit_price": 6.2,
"modifiers": [
{
"name": "Soy milk",
"price": 0.8
},
{
"name": "Extra hot",
"price": 0
}
],
"amount": 7
}
],
"payment": {
"method": "EFTPOS",
"entry_mode": "Contactless",
"card": "Debit •••• 4821",
"auth_code": "063914"
},
"loyalty": {
"program": "Coffee card",
"stamps": 7,
"card_size": 10
},
"footer_lines": [
"Books can be exchanged within 14 days with this receipt.",
"Poetry night: first Thursday of the month, 6.30 pm."
]
},
"output": {
"format": "pdf",
"filename": "receipt-thermal-80mm.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": {
"store": {
"name": "Wattle & Quill",
"tagline": "Books · Coffee · Paper goods",
"address": "Shop 3, 41 Jacaranda Arcade\nPaddington QLD 4064",
"abn": "12 345 678 901",
"website": "wattleandquill.example.com",
},
"sale": {
"transaction_id": "WQ2-260917-0142",
"datetime": "2026-09-17T08:42:00+10:00",
"timezone": "Australia/Brisbane",
"register": "Till 2",
"served_by": "Jasmin",
"order_type": "Eat in · Table 6",
"currency": "AUD",
"locale": "en-AU",
"gst_rate": 0.1,
},
"items": [
{
"name": "Flat white",
"variant": "Regular",
"quantity": 2,
"unit_price": 5.2,
"modifiers": [
{
"name": "Oat milk",
"price": 0.8,
},
],
"amount": 12,
},
{
"name": "Chai latte",
"variant": "Large",
"quantity": 1,
"unit_price": 6.2,
"modifiers": [
{
"name": "Soy milk",
"price": 0.8,
},
{
"name": "Extra hot",
"price": 0,
},
],
"amount": 7,
},
],
"payment": {
"method": "EFTPOS",
"entry_mode": "Contactless",
"card": "Debit •••• 4821",
"auth_code": "063914",
},
"loyalty": {
"program": "Coffee card",
"stamps": 7,
"card_size": 10,
},
"footer_lines": ["Books can be exchanged within 14 days with this receipt.", "Poetry night: first Thursday of the month, 6.30 pm."],
},
"output": {
"format": "pdf",
"filename": "receipt-thermal-80mm.pdf",
},
},
timeout=90,
)
response.raise_for_status()
print(response.json())