Skip to content

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.

A4 portraitLocale en-GBHTML + JinjaHeader & footer

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.

PathTypeExample
invoiceobject
invoice.numberstring"BF-2026-0147"
invoice.issue_datedate"2026-09-14"
invoice.due_datedate"2026-10-14"
invoice.currencystring"GBP"
invoice.buyer_referencestring"ORR-MKT-2291"
invoice.order_referencestring"PO-44812"
invoice.payment_termsstring"30 days net"
invoice.deliveryobject
invoice.delivery.datedate"2026-09-11"
invoice.sellerobject
invoice.seller.namestring"Brambleworth & Finch Design Studio"
invoice.seller.vat_idstring"GB123456789"
invoice.seller.electronic_addressobject
invoice.seller.electronic_address.idstring"accounts@brambleworth.example.com"
invoice.seller.electronic_address.schemestring"EM"
invoice.seller.addressobject
invoice.seller.address.line1string"Unit 4, Tannery Yard"
invoice.seller.address.line2string"14 Wharf Lane"
invoice.seller.address.citystring"Bristol"
invoice.seller.address.postcodestring"BS1 6XY"
invoice.seller.address.countrystring"GB"
invoice.seller.contactobject
invoice.seller.contact.namestring"Accounts team"
invoice.seller.contact.phonestring"+44 117 496 0321"
invoice.seller.contact.emailstring"accounts@brambleworth.example.com"
invoice.buyerobject
invoice.buyer.namestring"Orrery Instruments Ltd"
invoice.buyer.vat_idstring"GB987654321"
invoice.buyer.electronic_addressobject
invoice.buyer.electronic_address.idstring"invoices@orrery-instruments.example.com"
invoice.buyer.electronic_address.schemestring"EM"
invoice.buyer.addressobject
invoice.buyer.address.line1string"Floor 2, Mercator House"
invoice.buyer.address.line2string"27 Alderney Street"
invoice.buyer.address.citystring"Leeds"
invoice.buyer.address.postcodestring"LS2 8QR"
invoice.buyer.address.countrystring"GB"
invoice.buyer.contactobject
invoice.buyer.contact.namestring"Priya Castellane"
invoice.paymentobject
invoice.payment.means_codestring"30"
invoice.payment.remittance_informationstring"BF-2026-0147"
invoice.payment.credit_transfersarray<object> · 1
invoice.payment.credit_transfers[].account_idstring"GB52EXMP12345612345678"
invoice.payment.credit_transfers[].account_namestring"Brambleworth & Finch Design Studio"
invoice.payment.credit_transfers[].bicstring"EXMPGB2L"
invoice.linesarray<object> · 5
invoice.lines[].quantitystring"2"
invoice.lines[].unitstring"DAY"
invoice.lines[].priceobject
invoice.lines[].price.netstring"850.00"
invoice.lines[].taxobject
invoice.lines[].tax.categorystring"S"
invoice.lines[].tax.ratestring"20"
invoice.lines[].itemobject
invoice.lines[].item.namestring"Brand identity refresh — discovery workshops"
invoice.lines[].item.descriptionstring"Two half-day workshops and six stakeholder int…"
invoice.notesarray<object> · 2
invoice.notes[].textstring"Thank you for working with us on the autumn ra…"
websitestring"brambleworth.example.com"
bank_namestring"Westcombe Mutual Bank"
payment_linkstring"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.

data.json
{
  "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 %}

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"
  }
}'