Skip to content

210 × 297 mm

Invoices

E-Rechnung (ZUGFeRD / XRechnung)

A 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.

A4 portraitLocale de-DEHTML + 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
  • Grouping
  • 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: 1 top-level field, 62 paths in total. Send the same shape as data.

PathTypeExample
invoiceobject
invoice.numberstring"XR-2026-0007"
invoice.issue_datedate"2026-09-23"
invoice.due_datedate"2026-10-23"
invoice.currencystring"EUR"
invoice.buyer_referencestring"04011000-12345-34"
invoice.payment_termsstring"Zahlbar innerhalb von 30 Tagen ohne Abzug."
invoice.invoice_periodobject
invoice.invoice_period.startdate"2026-09-01"
invoice.invoice_period.enddate"2026-09-30"
invoice.sellerobject
invoice.seller.namestring"Größenwahn GmbH"
invoice.seller.vat_idstring"DE123456789"
invoice.seller.tax_numberstring"30/123/45678"
invoice.seller.legal_registrationobject
invoice.seller.legal_registration.idstring"HRB 12345"
invoice.seller.electronic_addressobject
invoice.seller.electronic_address.idstring"rechnung@groessenwahn.example"
invoice.seller.electronic_address.schemestring"EM"
invoice.seller.addressobject
invoice.seller.address.line1string"Beispielstraße 1"
invoice.seller.address.citystring"Berlin"
invoice.seller.address.postcodestring"10115"
invoice.seller.address.countrystring"DE"
invoice.seller.contactobject
invoice.seller.contact.namestring"Erika Muster"
invoice.seller.contact.phonestring"+49 30 1234567"
invoice.seller.contact.emailstring"erika.muster@groessenwahn.example"
invoice.buyerobject
invoice.buyer.namestring"Bundesamt für Beispiele"
invoice.buyer.electronic_addressobject
invoice.buyer.electronic_address.idstring"04011000-12345-34"
invoice.buyer.electronic_address.schemestring"0204"
invoice.buyer.addressobject
invoice.buyer.address.line1string"Amtsweg 2"
invoice.buyer.address.citystring"Bonn"
invoice.buyer.address.postcodestring"53113"
invoice.buyer.address.countrystring"DE"
invoice.paymentobject
invoice.payment.means_codestring"58"
invoice.payment.remittance_informationstring"XR-2026-0007"
invoice.payment.credit_transfersarray<object> · 1
invoice.payment.credit_transfers[].account_idstring"DE02120300000000202051"
invoice.payment.credit_transfers[].account_namestring"Größenwahn GmbH"
invoice.linesarray<object> · 2
invoice.lines[].quantitystring"8"
invoice.lines[].unitstring"HUR"
invoice.lines[].priceobject
invoice.lines[].price.netstring"95.00"
invoice.lines[].taxobject
invoice.lines[].tax.categorystring"S"
invoice.lines[].tax.ratestring"19"
invoice.lines[].itemobject
invoice.lines[].item.namestring"Softwarewartung"
invoice.lines[].item.descriptionstring"Wartung Fachverfahren, September 2026"
invoice.lines[].allowances?array<object> · 1
invoice.lines[].allowances[].amountstring"25.00"
invoice.lines[].allowances[].reasonstring"Rabatt"
invoice.notesarray<object> · 1
invoice.notes[].textstring"Leistungen gemäß Rahmenvertrag RV-2025-17."
invoice.notes[].subject_codestring"AAI"
invoice.contract_referencestring"RV-2025-17"

Sample data

Fictional records used for previews. Replace them with your own data when you call the API.

data.json
{
  "invoice": {
    "number": "XR-2026-0007",
    "issue_date": "2026-09-23",
    "due_date": "2026-10-23",
    "currency": "EUR",
    "buyer_reference": "04011000-12345-34",
    "payment_terms": "Zahlbar innerhalb von 30 Tagen ohne Abzug.",
    "invoice_period": {
      "start": "2026-09-01",
      "end": "2026-09-30"
    },
    "seller": {
      "name": "Größenwahn GmbH",
      "vat_id": "DE123456789",
      "tax_number": "30/123/45678",
      "legal_registration": {
        "id": "HRB 12345"
      },
      "electronic_address": {
        "id": "rechnung@groessenwahn.example",
        "scheme": "EM"
      },
      "address": {
        "line1": "Beispielstraße 1",
        "city": "Berlin",
        "postcode": "10115",
        "country": "DE"
      },
      "contact": {
        "name": "Erika Muster",
        "phone": "+49 30 1234567",
        "email": "erika.muster@groessenwahn.example"
      }
    },
    "buyer": {
      "name": "Bundesamt für Beispiele",
      "electronic_address": {
        "id": "04011000-12345-34",
        "scheme": "0204"
      },
      "address": {
        "line1": "Amtsweg 2",
        "city": "Bonn",
        "postcode": "53113",
        "country": "DE"
      }
    },
    "payment": {
      "means_code": "58",
      "remittance_information": "XR-2026-0007",
      "credit_transfers": [
        {
          "account_id": "DE02120300000000202051",
          "account_name": "Größenwahn GmbH"
        }
      ]
    },
    "lines": [
      {
        "quantity": "8",
        "unit": "HUR",
        "price": {
          "net": "95.00"
        },
        "tax": {
          "category": "S",
          "rate": "19"
        },
        "item": {
          "name": "Softwarewartung",
          "description": "Wartung Fachverfahren, September 2026"
        }
      },
      {
        "quantity": "1",
        "unit": "C62",
        "price": {
          "net": "250.00"
        },
        "tax": {
          "category": "S",
          "rate": "19"
        },
        "item": {
          "name": "Lizenz Modul Statistik"
        },
        "allowances": [
          {
            "amount": "25.00",
            "reason": "Rabatt"
          }
        ]
      }
    ],
    "notes": [
      {
        "text": "Leistungen gemäß Rahmenvertrag RV-2025-17.",
        "subject_code": "AAI"
      }
    ],
    "contract_reference": "RV-2025-17"
  }
}

Source

The body markup with Jinja, the stylesheet placed in <head> and the header and footer settings.

{#- E-Rechnung aus dem EN 16931-Rechnungsmodell (`invoice`, proto/einvoice/invoice.schema.json).
    Mit `output.einvoice.invoice_path = "invoice"` bettet der Renderer dieselben Daten als Factur-X/ZUGFeRD- bzw.
    XRechnung-XML ein; die Plattform berechnet die Beträge für jedes Rendering als `render.invoice` (mit oder ohne
    XML, proto/README.md §20.7) — so druckt das Template genau die Beträge des XML. `render.einvoice` zeigt nur an,
    ob das XML eingebettet ist. Ohne beides (z. B. als reines HTML gerendert) rechnet es mit denselben Regeln selbst. -#}
{%- set inv = invoice -%}
{%- set cur = inv.currency -%}
{%- set loc = "de-DE" -%}
{%- 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": "Std.", "C62": "Stk.", "H87": "Stk.", "DAY": "Tag(e)", "WEE": "Woche(n)", "MON": "Monat(e)", "ANN": "Jahr(e)", "KGM": "kg", "MTR": "m", "LTR": "l", "LS": "pauschal", "E48": "Leistung"} -%}
{%- set means = {"58": "SEPA-Überweisung", "30": "Überweisung", "59": "SEPA-Lastschrift", "49": "Lastschrift", "48": "Kartenzahlung", "54": "Kreditkarte", "10": "Barzahlung"} -%}
{%- set exemptions = {"Z": "Nullsatz", "E": "Steuerbefreit", "AE": "Steuerschuldnerschaft des Leistungsempfängers", "K": "Innergemeinschaftliche Lieferung", "G": "Ausfuhrlieferung", "O": "Nicht steuerbar"} -%}
{%- set title = "Gutschrift" if inv.type_code in ["381", "261"] else ("Korrigierte Rechnung" if inv.type_code == "384" else "Rechnung") -%}

{#- Zeilenbeträge und USt-Gruppen (EN 16931 BR-CO: Zeile = Menge × Preis ÷ Basismenge ± Zu-/Abschläge) -#}
{%- 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, "exemption_reason": none}] -%}
    {%- 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 -%}

{%- macro money(value) -%}{{ value | format_currency(cur, loc) }}{%- endmacro -%}
{%- macro day(value) -%}{{ value | format_date("medium", loc) }}{%- endmacro -%}
{%- macro one_line(party) -%}
  {{ party.name }}{% if party.address.line1 %} · {{ party.address.line1 }}{% endif %} · {{ party.address.postcode }} {{ party.address.city }}
{%- endmacro -%}

<header class="top">
  <div class="brand">
    <svg viewBox="0 0 40 40" aria-hidden="true">
      <rect x="2" y="2" width="36" height="36" rx="8" fill="#1f5f8b"/>
      <path d="M12 12h16M12 20h16M12 28h10" fill="none" stroke="#fff" stroke-width="3.2" stroke-linecap="round"/>
    </svg>
    <div>
      <div class="brand-name">{{ inv.seller.trading_name or inv.seller.name }}</div>
      {% if inv.seller.contact %}<div class="muted">{{ inv.seller.contact.email }}{% if inv.seller.contact.phone %} · {{ inv.seller.contact.phone }}{% endif %}</div>{% endif %}
    </div>
  </div>
  <table class="facts">
    <tr><th scope="row">{{ title }}snummer</th><td><strong>{{ inv.number }}</strong></td></tr>
    <tr><th scope="row">Datum</th><td>{{ day(inv.issue_date) }}</td></tr>
    {% if inv.delivery and inv.delivery.date %}<tr><th scope="row">Lieferdatum</th><td>{{ day(inv.delivery.date) }}</td></tr>{% endif %}
    {% if inv.invoice_period %}<tr><th scope="row">Leistungszeitraum</th><td>{{ day(inv.invoice_period.start) }} – {{ day(inv.invoice_period.end) }}</td></tr>{% endif %}
    {% if inv.due_date %}<tr><th scope="row">Fällig am</th><td>{{ day(inv.due_date) }}</td></tr>{% endif %}
    {% if inv.buyer_reference %}<tr><th scope="row">{{ "Leitweg-ID" if (inv.buyer.electronic_address and inv.buyer.electronic_address.scheme == "0204") else "Ihre Referenz" }}</th><td>{{ inv.buyer_reference }}</td></tr>{% endif %}
    {% if inv.order_reference %}<tr><th scope="row">Bestellnummer</th><td>{{ inv.order_reference }}</td></tr>{% endif %}
    {% if inv.contract_reference %}<tr><th scope="row">Vertrag</th><td>{{ inv.contract_reference }}</td></tr>{% endif %}
    {% if inv.buyer.vat_id %}<tr><th scope="row">Ihre USt-IdNr.</th><td>{{ inv.buyer.vat_id }}</td></tr>{% endif %}
  </table>
</header>

<p class="sender-line">{{ one_line(inv.seller) }}</p>
<div class="recipient">
  <p><strong>{{ inv.buyer.name }}</strong></p>
  {% if inv.buyer.contact and inv.buyer.contact.name %}<p>z. Hd. {{ inv.buyer.contact.name }}</p>{% endif %}
  {% if inv.buyer.address.line1 %}<p>{{ inv.buyer.address.line1 }}</p>{% endif %}
  {% if inv.buyer.address.line2 %}<p>{{ inv.buyer.address.line2 }}</p>{% endif %}
  <p>{{ inv.buyer.address.postcode }} {{ inv.buyer.address.city }}</p>
  {% if inv.buyer.address.country != "DE" %}<p>{{ inv.buyer.address.country }}</p>{% endif %}
</div>

<h1>{{ title }} {{ inv.number }}</h1>
{% if inv.preceding_invoices %}
<p class="intro">Bezug: {% for p in inv.preceding_invoices %}Rechnung {{ p.number }}{% if p.issue_date %} vom {{ day(p.issue_date) }}{% endif %}{{ ", " if not loop.last }}{% endfor %}</p>
{% else %}
<p class="intro">Wir berechnen Ihnen die folgenden Leistungen.</p>
{% endif %}

<table class="items">
  <thead>
    <tr>
      <th class="pos" scope="col">Pos.</th>
      <th scope="col">Bezeichnung</th>
      <th class="num" scope="col">Menge</th>
      <th class="num" scope="col">Einzelpreis</th>
      <th class="num" scope="col">USt</th>
      <th class="num" scope="col">Betrag</th>
    </tr>
  </thead>
  <tbody>
    {% for line in inv.lines %}
    <tr>
      <td class="pos">{{ line.id or loop.index }}</td>
      <td>
        <span class="item-name">{{ line.item.name }}</span>
        {% if line.item.description %}<span class="item-detail">{{ line.item.description }}</span>{% endif %}
        {% if line.period %}<span class="item-detail">Zeitraum {{ day(line.period.start) }} – {{ day(line.period.end) }}</span>{% endif %}
        {% for a in line.allowances | default([]) %}<span class="item-detail">abzüglich {{ a.reason or "Nachlass" }}: {{ money(a.amount | float) }}</span>{% endfor %}
        {% for c in line.charges | default([]) %}<span class="item-detail">zuzüglich {{ c.reason or "Zuschlag" }}: {{ money(c.amount | float) }}</span>{% endfor %}
      </td>
      <td class="num">{{ (line.quantity | float) | format_number(loc) }} {{ units[line.unit or "C62"] or line.unit }}</td>
      <td class="num">{{ money(line.price.net | float) }}</td>
      <td class="num">{% if line.tax.category in ["S", "L", "M"] %}{{ (line.tax.rate | float) | format_number(loc) }} %{% else %}{{ line.tax.category }}{% endif %}</td>
      <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 %}
      <p class="exemption">{{ exemptions[b.category] }}{% if inv.vat_exemptions and inv.vat_exemptions[b.category] and inv.vat_exemptions[b.category].reason %}: {{ inv.vat_exemptions[b.category].reason }}{% endif %}</p>
      {% endif %}
    {% endfor %}
  </div>
  <table class="totals">
    <tr><th scope="row">Summe Positionen</th><td class="num">{{ money(totals.line_total | float) }}</td></tr>
    {% if (totals.allowance_total | float) != 0 %}<tr><th scope="row">Nachlässe</th><td class="num">−{{ money(totals.allowance_total | float) }}</td></tr>{% endif %}
    {% if (totals.charge_total | float) != 0 %}<tr><th scope="row">Zuschläge</th><td class="num">{{ money(totals.charge_total | float) }}</td></tr>{% endif %}
    <tr class="sub"><th scope="row">Nettobetrag</th><td class="num">{{ money(totals.tax_basis_total | float) }}</td></tr>
    {% for b in breakdown %}
    <tr><th scope="row">{% if b.category in exemptions %}{{ exemptions[b.category] }} ({{ money(b.basis | float) }}){% else %}USt {{ (b.rate | float) | format_number(loc) }} % auf {{ money(b.basis | float) }}{% endif %}</th><td class="num">{{ money(b.tax | float) }}</td></tr>
    {% endfor %}
    <tr class="grand"><th scope="row">Gesamtbetrag</th><td class="num">{{ money(totals.grand_total | float) }}</td></tr>
    {% if (totals.prepaid | float) != 0 %}<tr><th scope="row">Bereits gezahlt</th><td class="num">−{{ money(totals.prepaid | float) }}</td></tr>{% endif %}
    {% if (totals.rounding | float) != 0 %}<tr><th scope="row">Rundung</th><td class="num">{{ money(totals.rounding | float) }}</td></tr>{% endif %}
    <tr class="due"><th scope="row">Zahlbetrag</th><td class="num">{{ money(totals.due_payable | float) }}</td></tr>
  </table>
</section>

{% if inv.payment %}
<section class="payment">
  <p class="label">Zahlung</p>
  <dl>
    <dt>Zahlungsart</dt><dd>{{ means[inv.payment.means_code] or inv.payment.means_text or inv.payment.means_code }}</dd>
    {% for account in inv.payment.credit_transfers | default([]) %}
    <dt>Kontoinhaber</dt><dd>{{ account.account_name or inv.seller.name }}</dd>
    <dt>IBAN</dt><dd>{{ account.account_id }}</dd>
    {% if account.bic %}<dt>BIC</dt><dd>{{ account.bic }}</dd>{% endif %}
    {% endfor %}
    {% if inv.payment.direct_debit %}
    <dt>Mandatsreferenz</dt><dd>{{ inv.payment.direct_debit.mandate_reference }}</dd>
    <dt>Gläubiger-ID</dt><dd>{{ inv.payment.direct_debit.creditor_id }}</dd>
    {% endif %}
    <dt>Verwendungszweck</dt><dd>{{ inv.payment.remittance_information or inv.number }}</dd>
    {% if inv.payment_terms %}<dt>Bedingungen</dt><dd>{{ inv.payment_terms }}</dd>{% endif %}
  </dl>
</section>
{% endif %}

{% if embedded %}
<p class="einvoice-hint">Diese Rechnung enthält die strukturierten Rechnungsdaten nach EN 16931 ({{ embedded.profile }}) als eingebettete XML-Datei.</p>
{% 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": "XR-2026-0007",
      "issue_date": "2026-09-23",
      "due_date": "2026-10-23",
      "currency": "EUR",
      "buyer_reference": "04011000-12345-34",
      "payment_terms": "Zahlbar innerhalb von 30 Tagen ohne Abzug.",
      "invoice_period": {
        "start": "2026-09-01",
        "end": "2026-09-30"
      },
      "seller": {
        "name": "Größenwahn GmbH",
        "vat_id": "DE123456789",
        "tax_number": "30/123/45678",
        "legal_registration": {
          "id": "HRB 12345"
        },
        "electronic_address": {
          "id": "rechnung@groessenwahn.example",
          "scheme": "EM"
        },
        "address": {
          "line1": "Beispielstraße 1",
          "city": "Berlin",
          "postcode": "10115",
          "country": "DE"
        },
        "contact": {
          "name": "Erika Muster",
          "phone": "+49 30 1234567",
          "email": "erika.muster@groessenwahn.example"
        }
      },
      "buyer": {
        "name": "Bundesamt für Beispiele",
        "electronic_address": {
          "id": "04011000-12345-34",
          "scheme": "0204"
        },
        "address": {
          "line1": "Amtsweg 2",
          "city": "Bonn",
          "postcode": "53113",
          "country": "DE"
        }
      },
      "payment": {
        "means_code": "58",
        "remittance_information": "XR-2026-0007",
        "credit_transfers": [
          {
            "account_id": "DE02120300000000202051",
            "account_name": "Größenwahn GmbH"
          }
        ]
      },
      "lines": [
        {
          "quantity": "8",
          "unit": "HUR",
          "price": {
            "net": "95.00"
          },
          "tax": {
            "category": "S",
            "rate": "19"
          },
          "item": {
            "name": "Softwarewartung",
            "description": "Wartung Fachverfahren, September 2026"
          }
        },
        {
          "quantity": "1",
          "unit": "C62",
          "price": {
            "net": "250.00"
          },
          "tax": {
            "category": "S",
            "rate": "19"
          },
          "item": {
            "name": "Lizenz Modul Statistik"
          },
          "allowances": [
            {
              "amount": "25.00",
              "reason": "Rabatt"
            }
          ]
        }
      ],
      "notes": [
        {
          "text": "Leistungen gemäß Rahmenvertrag RV-2025-17.",
          "subject_code": "AAI"
        }
      ],
      "contract_reference": "RV-2025-17"
    }
  },
  "output": {
    "format": "pdf",
    "filename": "invoice-e-rechnung.pdf"
  }
}'