# E-invoicing

> Factur-X and ZUGFeRD PDFs with embedded EN 16931 XML, XRechnung export in CII or UBL, the invoice model, computed totals and validation reports.



Dynamic Document API turns one invoice model into e-invoices that pass the official validators. Two outputs are available:

* **Hybrid PDFs** (Factur-X, ZUGFeRD): your usual invoice PDF, converted to PDF/A-3b, with the invoice XML embedded.
* **Standalone XML** (XRechnung, EN 16931): the XML on its own, in CII or UBL syntax.

Every file is validated before it is delivered, and the validation report comes back with the render.

| Output                    | Request                                        | Format                                                                             |
| ------------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------- |
| Factur-X / ZUGFeRD PDF    | `output.einvoice` on any PDF render            | PDF/A-3b with embedded UN/CEFACT CII XML (Factur-X 1.09, identical to ZUGFeRD 2.x) |
| XRechnung PDF             | `output.einvoice` with the profile `XRECHNUNG` | PDF/A-3b with an embedded XRechnung 3.0 CII file, `xrechnung.xml`                  |
| XRechnung or EN 16931 XML | `POST /v1/einvoices`                           | CII (UN/CEFACT D16B) or UBL 2.1                                                    |

The XML follows the European semantic model EN 16931-1. It is checked with the KoSIT validator, which applies the XRechnung, EN 16931 and Factur-X rules. PDFs are checked with veraPDF. Neither validator is modified.

E-invoicing is included in the Growth, Pro, Scale and Enterprise plans. On Free and Starter, requests with `output.einvoice` and calls to `POST /v1/einvoices` fail with `402 plan_feature_unavailable`, and a template's [default e-invoice](#make-a-template-an-e-invoice-by-default) is skipped with a warning. See [Plans and limits](/docs/plans-and-limits).

> **What we guarantee**
>
> Dynamic Document API guarantees technical format conformance, validated against the official schemas and schematron rules. You remain responsible for the content of your invoices, their tax correctness and their transmission, for example through a certified platform (PDP/PA) in France or the e-invoicing portals of German public authorities.

## Profiles [#profiles]

A profile decides which information the XML carries and which rules apply. Pick the one your recipient asks for. `EN16931` is the right default for B2B invoices in the EU, and `XRECHNUNG` is the standard for German public-sector buyers.

| Profile     | Guideline ID (BT-24)                                                    | Invoice lines   | Embedded file   | `AFRelationship` | Syntaxes |
| ----------- | ----------------------------------------------------------------------- | --------------- | --------------- | ---------------- | -------- |
| `MINIMUM`   | `urn:factur-x.eu:1p0:minimum`                                           | not transmitted | `factur-x.xml`  | `Data`           | CII      |
| `BASIC_WL`  | `urn:factur-x.eu:1p0:basicwl`                                           | not transmitted | `factur-x.xml`  | `Data`           | CII      |
| `BASIC`     | `urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic`           | basic subset    | `factur-x.xml`  | `Alternative`    | CII      |
| `EN16931`   | `urn:cen.eu:en16931:2017`                                               | complete        | `factur-x.xml`  | `Alternative`    | CII, UBL |
| `EXTENDED`  | `urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended`       | complete        | `factur-x.xml`  | `Alternative`    | CII      |
| `XRECHNUNG` | `urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0` | complete        | `xrechnung.xml` | `Alternative`    | CII, UBL |

* `MINIMUM` and `BASIC_WL` carry header data and totals only. They don't satisfy EN 16931, so German B2B rules don't accept them as e-invoices. Use them only where a booking aid is enough.
* The PDF's XMP metadata names the profile in `fx:ConformanceLevel`, as the Factur-X specification requires (for example `EN 16931` or `BASIC WL`).
* UBL is available for standalone XML only. Hybrid PDFs always embed CII.

## Create a Factur-X or ZUGFeRD PDF [#create-a-factur-x-or-zugferd-pdf]

Add `output.einvoice` to any PDF render: template, HTML, URL or Markdown. The page renders as usual. Then the engine:

1. generates the XML from the invoice model and validates it
2. converts the PDF to PDF/A-3b and embeds the XML as an attachment
3. writes the Factur-X metadata and validates the finished PDF with veraPDF

The invoice model can sit in your template data. `invoice_path` points at it, so the template and the XML use the same numbers:

<CodeBlockTabs defaultValue="cURL">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="cURL">
      cURL
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="Python">
      Python
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="cURL">
    ```bash
    curl https://api-eu.dynamicdocumentapi.com/v1/renders \
      -H "Authorization: Bearer $DYNAMIC_DOCUMENT_API_KEY" \
      -H "Idempotency-Key: invoice-R-2026-0042" \
      -H "Content-Type: application/json" \
      -d '{
        "input": { "type": "template", "template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C" },
        "data": {
          "invoice": {
            "number": "R-2026-0042",
            "issue_date": "2026-09-23",
            "due_date": "2026-10-23",
            "currency": "EUR",
            "buyer_reference": "PO-4711",
            "seller": {
              "name": "Example GmbH",
              "vat_id": "DE123456789",
              "electronic_address": { "id": "invoices@example.com", "scheme": "EM" },
              "address": { "line1": "Beispielstraße 1", "city": "Berlin", "postcode": "10115", "country": "DE" }
            },
            "buyer": {
              "name": "Customer AG",
              "vat_id": "DE987654321",
              "electronic_address": { "id": "ap@customer.example", "scheme": "EM" },
              "address": { "line1": "Marktplatz 5", "city": "München", "postcode": "80331", "country": "DE" }
            },
            "payment": { "means_code": "58", "credit_transfers": [{ "account_id": "DE02120300000000202051" }] },
            "lines": [
              { "quantity": "10", "unit": "HUR", "price": { "net": "100.00" }, "tax": { "category": "S", "rate": "19" }, "item": { "name": "Consulting" } }
            ]
          }
        },
        "output": {
          "format": "pdf",
          "filename": "invoice-{{ data.invoice.number }}.pdf",
          "einvoice": { "profile": "EN16931", "invoice_path": "invoice" }
        }
      }'
    ```
  </CodeBlockTab>

  <CodeBlockTab value="Python">
    ```python
    import os

    import requests

    invoice = {
        "number": "R-2026-0042",
        "issue_date": "2026-09-23",
        "due_date": "2026-10-23",
        "currency": "EUR",
        "buyer_reference": "PO-4711",
        "seller": {
            "name": "Example GmbH",
            "vat_id": "DE123456789",
            "electronic_address": {"id": "invoices@example.com", "scheme": "EM"},
            "address": {"line1": "Beispielstraße 1", "city": "Berlin", "postcode": "10115", "country": "DE"},
        },
        "buyer": {
            "name": "Customer AG",
            "vat_id": "DE987654321",
            "electronic_address": {"id": "ap@customer.example", "scheme": "EM"},
            "address": {"line1": "Marktplatz 5", "city": "München", "postcode": "80331", "country": "DE"},
        },
        "payment": {"means_code": "58", "credit_transfers": [{"account_id": "DE02120300000000202051"}]},
        "lines": [
            {
                "quantity": "10",
                "unit": "HUR",
                "price": {"net": "100.00"},
                "tax": {"category": "S", "rate": "19"},
                "item": {"name": "Consulting"},
            }
        ],
    }

    response = requests.post(
        "https://api-eu.dynamicdocumentapi.com/v1/renders",
        headers={
            "Authorization": f"Bearer {os.environ['DYNAMIC_DOCUMENT_API_KEY']}",
            "Idempotency-Key": f"invoice-{invoice['number']}",
        },
        json={
            "input": {"type": "template", "template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C"},
            "data": {"invoice": invoice},
            "output": {
                "format": "pdf",
                "filename": "invoice-{{ data.invoice.number }}.pdf",
                "einvoice": {"profile": "EN16931", "invoice_path": "invoice"},
            },
        },
        timeout=130,
    )
    render = response.json()
    print(render["status"], render.get("result"))
    ```
  </CodeBlockTab>
</CodeBlockTabs>

The [E-Rechnung template](/templates/invoices/invoice-e-rechnung) in the gallery is built for this. It reads the invoice model from `invoice`, prints the computed amounts and works with every profile.

| Field                          | Description                                                                                                                                         |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `output.einvoice.profile`      | Required. One of the [profiles](#profiles)                                                                                                          |
| `output.einvoice.invoice`      | The [invoice model](#the-invoice-model) itself                                                                                                      |
| `output.einvoice.invoice_path` | A dotted path into `data` that holds the invoice model, such as `"invoice"` or `"order.invoice"`. Send exactly one of `invoice` and `invoice_path`. |
| `output.einvoice.syntax`       | Omit it, or send `"cii"`. Hybrid PDFs always embed CII.                                                                                             |

Rules checked before anything renders. Each is a `400 validation_error` with a JSON Pointer to the field:

* `output.format` must be `pdf`. An e-invoice PDF is always PDF/A-3b, so `pdf.pdfa` must be omitted or `"3b"`.
* `pdf.protect` can't be combined with `output.einvoice`, because PDF/A forbids encryption.
* `invoice_path` must resolve to an object in `data`. It can't be combined with `data_url`, because the data isn't available when the request is checked; send `invoice` instead. (A template's [default e-invoice](#make-a-template-an-e-invoice-by-default) works with `data_url`.)
* The invoice is validated against the model's JSON Schema, for example `/output/einvoice/invoice/lines/0/tax/category`.
* Editor previews reject `output.einvoice`. Test renders accept it; see [test mode](#test-mode).

In [batches](/docs/batches), a literal `invoice` applies to every item. `invoice_path` is resolved in each item's own data, so every row can carry its own invoice.

### Print the computed amounts [#print-the-computed-amounts]

When a render embeds an e-invoice, or its template has a [default e-invoice](#make-a-template-an-e-invoice-by-default), the engine computes the invoice before the template runs and passes the results to the template as `render.invoice`. Print these values instead of calculating totals in the template, and the visible invoice always matches the XML to the cent:

```jinja
{% set e = render.invoice %}
<table>
  {% for line in invoice.lines %}
  <tr>
    <td>{{ line.item.name }}</td>
    <td>{{ line.quantity }}</td>
    <td>{{ e.lines[loop.index0].net_amount | format_currency(e.currency, "de-DE") }}</td>
  </tr>
  {% endfor %}
</table>
{% for vat in e.vat_breakdown %}
<p>VAT {{ vat.rate }} % on {{ vat.basis | format_currency(e.currency, "de-DE") }}: {{ vat.tax | format_currency(e.currency, "de-DE") }}</p>
{% endfor %}
<p><strong>Total due: {{ e.totals.due_payable | format_currency(e.currency, "de-DE") }}</strong></p>
```

| Field                              | Description                                                                                                                          |
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `number`, `currency`               | From the invoice                                                                                                                     |
| `lines[].id`, `lines[].net_amount` | Line ID and line net amount (BT-131), in the order of `invoice.lines`                                                                |
| `vat_breakdown[]`                  | One entry per VAT category and rate: `category`, `rate`, `basis`, `tax`, `exemption_reason`, `exemption_reason_code`                 |
| `totals`                           | `line_total`, `allowance_total`, `charge_total`, `tax_basis_total`, `tax_total`, `grand_total`, `prepaid`, `rounding`, `due_payable` |

Amounts are strings with two decimals, such as `"1190.00"`, which `format_currency` accepts.

`render.invoice` is there in every render that embeds an e-invoice (`output.einvoice`, or the template's [default](#make-a-template-an-e-invoice-by-default)), and in every render of a template with a default e-invoice that doesn't embed it: the opt-out below, plans without e-invoicing, image output, editor and dashboard previews. The computation is free, so the template needs no fallback and no arithmetic of its own.

`render.einvoice` holds the same values plus `profile`, but only when the XML is embedded. Use it to tell the two apart, for example for a note on the page:

```jinja
{% if render.einvoice %}<p>This PDF carries an embedded {{ render.einvoice.profile }} e-invoice.</p>{% endif %}
```

An invoice that can't be computed fails an e-invoice render with `einvoice_invalid` before the template runs. Where nothing is embedded, it never fails the render: `render.invoice` is left out and the render carries the warning `invoice_totals_unavailable`, whose message names the first problem.

## Make a template an e-invoice by default [#make-a-template-an-e-invoice-by-default]

A PDF template (code or Markdown) can store the e-invoice in its default options. Every render of it is then a Factur-X / ZUGFeRD PDF, and callers only send the data:

```json
{
  "input": { "type": "template", "template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C" },
  "data": { "invoice": { "number": "R-2026-0042", "issue_date": "2026-09-23", "currency": "EUR", "…": "…" } }
}
```

Turn it on in the dashboard under the template's **Options** → &#x2A;*E-invoice (ZUGFeRD / Factur-X)**: pick the profile (`EN16931` by default) and the data path (`invoice` by default). Through the API, it is `einvoice` in the template's `options`, next to `output_format` and `pdf`:

```json
"options": {
  "output_format": "pdf",
  "pdf": { "paper": { "size": "A4" } },
  "einvoice": { "profile": "EN16931", "invoice_path": "invoice" }
}
```

A template default holds a profile and a data path, never an invoice. It is checked when the template is saved: `profile` must be one of the [profiles](#profiles), `invoice_path` a dotted data path, `output_format` must be `pdf`, and `pdf.protect` isn't allowed with it. A default `pdf.pdfa` of `"2b"` is raised to `"3b"` at render time. Canvas templates can't store an e-invoice; send `output.einvoice` with the request instead. Every invoice template in the [gallery](/templates) ships with `{"profile": "EN16931", "invoice_path": "invoice"}`, and "Use this template" keeps it.

What a request sends decides:

| Request                          | Result                                                                               |
| -------------------------------- | ------------------------------------------------------------------------------------ |
| No `output.einvoice`             | The template's default applies, with the same checks as `output.einvoice`            |
| `output.einvoice` with an object | Replaces the default completely, for example another profile or path                 |
| `"output": { "einvoice": null }` | Turns the default off: a plain PDF (`"einvoice": null` in the convenience endpoints) |

The same applies to batches (`POST /v1/batches`): the default's `invoice_path` is resolved in each item's data, an item without an invoice object there rejects the batch with `400 validation_error` at `/items/<i>/data/<path>` (`/csv/row/<n>/data/<path>` for CSV rows, whose text cells can't hold the invoice's `lines`, so send JSON `items`), and `"output": {"einvoice": null}` turns the default off for every item. In the dashboard's batch wizard, that is the **Embed the e-invoice** checkbox on the review step. Other differences from a request-level `output.einvoice`:

* **Missing invoice.** If the path doesn't lead to an object in the request's `data`, the request fails with `400 validation_error` at `/data`, and the message names the opt-out. Model errors point into `data`, for example `/data/invoice/seller/name`.
* **`data_url` works.** The data isn't available when the request is checked, so the renderer resolves the path. If it finds no invoice there, the render fails with `einvoice_invalid`.
* **Plans without e-invoicing.** On Free and Starter the default is skipped: the render is a normal PDF and carries the warning `einvoice_skipped_plan`. A request that sends `output.einvoice` itself still fails with `402 plan_feature_unavailable`.
* **Other formats.** The default only applies to PDF output: rendering such a template as PNG, JPG or WebP gives a plain image without an e-invoice.
* **Previews.** Editor previews and the publish test render ignore the default and stay plain PDFs.

In all of these cases without an embedded e-invoice, the template still gets the [computed amounts](#print-the-computed-amounts) as `render.invoice`, at no cost.

## Export XRechnung or EN 16931 XML [#export-xrechnung-or-en-16931-xml]

`POST /v1/einvoices` returns the XML on its own, without a PDF. It creates a regular render with one `application/xml` file, and accepts the usual `mode`, `delivery`, `webhook`, `reference`, `metadata` and `test` fields and the `Idempotency-Key` header:

```bash
curl https://api-eu.dynamicdocumentapi.com/v1/einvoices \
  -H "Authorization: Bearer $DYNAMIC_DOCUMENT_API_KEY" \
  -H "Idempotency-Key: xrechnung-XR-2026-0007" \
  -H "Content-Type: application/json" \
  -d @xrechnung-request.json \
  -o XR-2026-0007.xml
```

`xrechnung-request.json` holds the profile, the syntax and the invoice, for example the one in [The invoice model](#the-invoice-model) (shortened here):

```json
{
  "profile": "XRECHNUNG",
  "syntax": "ubl",
  "filename": "XR-2026-0007.xml",
  "invoice": { "number": "XR-2026-0007", "issue_date": "2026-09-23", "currency": "EUR", "seller": {}, "buyer": {}, "lines": [] },
  "delivery": { "type": "binary" }
}
```

| Field      | Description                                                                    |
| ---------- | ------------------------------------------------------------------------------ |
| `profile`  | Required. One of the [profiles](#profiles)                                     |
| `syntax`   | `cii` (default) or `ubl`. UBL is available for `EN16931` and `XRECHNUNG` only. |
| `invoice`  | Required. The [invoice model](#the-invoice-model)                              |
| `filename` | Defaults to the invoice number, cleaned up for use as a file name, plus `.xml` |

With `delivery.type: "binary"` the response body is the XML file itself. Otherwise you get the render object: its `input_type` is `einvoice`, its `output_format` is `xml`, and it carries the validation report in `result`. In UBL, credit notes (type code `381`) use the `CreditNote` root element and everything else uses `Invoice`.

## The invoice model [#the-invoice-model]

The invoice model is JSON for the EN 16931 semantic model, with snake\_case names. Every field corresponds to one business term (BT) or business group (BG) of the standard, and the [API reference](/docs/api-reference) lists all of them. This complete example is a valid XRechnung for a German federal authority:

```json
{
  "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" }]
    }
  ]
}
```

### Conventions [#conventions]

| Topic                 | Convention                                                                                                                                                                                             |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Amounts               | Decimal strings such as `"95.00"`, with at most two decimals. JSON numbers are accepted, but strings avoid floating-point surprises.                                                                   |
| Quantities and prices | Up to six decimals, for example `"0.125"`                                                                                                                                                              |
| Dates                 | `YYYY-MM-DD`                                                                                                                                                                                           |
| Codes                 | Currencies in ISO 4217 (`EUR`), countries in ISO 3166-1 alpha-2 (`DE`), units from UN/ECE Recommendation 20 (`C62` one, `H87` piece, `HUR` hour, `DAY` day, `KGM` kilogram). `unit` defaults to `C62`. |
| Electronic addresses  | `id` plus an EAS `scheme`, such as `EM` for an e-mail address, `0204` for a German Leitweg-ID or `9930` for a German VAT ID                                                                            |
| Document type         | `type_code` (BT-3) defaults to `380`, a commercial invoice. Others include `381` credit note, `384` corrected invoice, `389` self-billed invoice, `326` partial invoice and `386` prepayment invoice.  |

### Required fields [#required-fields]

Every invoice needs `number`, `issue_date`, `currency`, `seller` (with `name` and `address`), `buyer` (with `name` and `address`) and at least one line. Each line needs `quantity`, `price.net`, `tax.category` and `item.name`, and a `tax.rate` unless its category is `O`. `MINIMUM` and `BASIC_WL` don't transmit lines, but they still need them to compute the totals.

The profiles add their own rules. For `XRECHNUNG`:

* `buyer_reference` (BT-10), the Leitweg-ID for German public buyers
* `seller.contact` with `name`, `phone` and `email`
* `electronic_address` for both the seller and the buyer
* `payment` with a payment means code, for example `58` for a SEPA credit transfer, and the account

`process_id` (BT-23) defaults to `urn:fdc:peppol.eu:2017:poacc:billing:01:1.0` for `XRECHNUNG`.

### VAT categories [#vat-categories]

| Category | Meaning                | Rate                   | Also required                                                      |
| -------- | ---------------------- | ---------------------- | ------------------------------------------------------------------ |
| `S`      | Standard rate          | the rate, such as `19` | —                                                                  |
| `Z`      | Zero rated goods       | `0`                    | —                                                                  |
| `E`      | Exempt from VAT        | `0`                    | `vat_exemptions.E`                                                 |
| `AE`     | Reverse charge         | `0`                    | `vat_exemptions.AE`, seller and buyer VAT IDs                      |
| `K`      | Intra-community supply | `0`                    | `vat_exemptions.K`, seller and buyer VAT IDs, delivery information |
| `G`      | Export outside the EU  | `0`                    | `vat_exemptions.G`                                                 |
| `O`      | Not subject to VAT     | none                   | `vat_exemptions.O`                                                 |
| `L`      | Canary Islands IGIC    | the IGIC rate          | —                                                                  |
| `M`      | Ceuta and Melilla IPSI | the IPSI rate          | —                                                                  |

`vat_exemptions` gives the exemption reason (BT-120) and code (BT-121) for each category. This is a reverse-charge supply to a customer in another EU country:

```json
{
  "vat_exemptions": { "AE": { "reason": "Reverse charge", "reason_code": "vatex-eu-ae" } },
  "charges": [{ "amount": "15.00", "tax_category": "AE", "tax_rate": "0", "reason": "Versand", "reason_code": "FC" }],
  "lines": [
    { "quantity": "12", "unit": "H87", "price": { "net": "49.50" }, "tax": { "category": "AE", "rate": "0" }, "item": { "name": "Druckkopf X2" } }
  ]
}
```

Document-level `allowances` and `charges` need their own `tax_category` and `tax_rate`, because they change the VAT basis of that category.

### Credit notes [#credit-notes]

Set `type_code` to `"381"` and reference the original invoice in `preceding_invoices`. Quantities and prices stay positive, because the type code is what makes the document a credit:

```json
{
  "number": "GS-2026-0003",
  "type_code": "381",
  "issue_date": "2026-09-23",
  "preceding_invoices": [{ "number": "R-2026-0042", "issue_date": "2026-09-23" }]
}
```

### Computed fields [#computed-fields]

Don't send line totals, the VAT breakdown or document totals. The engine computes them from the lines, allowances and charges, using the EN 16931 rules:

| Value                     | Business terms | Rule                                                                                                                   |
| ------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Line net amount           | BT-131         | quantity × net price ÷ base quantity + line charges − line allowances                                                  |
| VAT per category and rate | BT-116, BT-117 | basis = the lines, allowances and charges of that category and rate; tax = basis × rate ÷ 100, rounded to two decimals |
| Sum of line net amounts   | BT-106         | sum of the line net amounts                                                                                            |
| Total without VAT         | BT-109         | lines − document allowances + document charges                                                                         |
| Total VAT                 | BT-110         | sum of the VAT amounts per category                                                                                    |
| Total with VAT            | BT-112         | total without VAT + total VAT                                                                                          |
| Amount due                | BT-115         | total with VAT − `prepaid_amount` + `rounding_amount`                                                                  |

For the example above: 8 × 95.00 = 760.00 and 250.00 − 25.00 = 225.00, so the lines total 985.00. VAT at 19 % on 985.00 is 187.15, and 1,172.15 is due.

A line may carry its own `net_amount`. It isn't needed, but when present the engine checks it against the computed value and rejects the invoice if they differ.

## Validation report [#validation-report]

PDF/A and e-invoice renders carry a conformance report in `result.conformance`. Succeeded renders have one, and so do renders that failed validation:

```json
{
  "result": {
    "conformance": {
      "pdfa": {
        "level": "3b",
        "status": "passed",
        "validator": "veraPDF 1.30.2",
        "profile": "PDF/A-3B validation profile",
        "errors": [],
        "warnings": []
      },
      "einvoice": {
        "profile": "XRECHNUNG",
        "syntax": "cii",
        "status": "passed",
        "validator": "KoSIT Validator 1.6.3",
        "scenario": "EN16931 XRechnung (CII)",
        "errors": [],
        "warnings": []
      }
    }
  }
}
```

A render that failed because the invoice lacks a field the profile requires carries the same object with `status: "failed"`, and its findings point at the invoice:

```json
{
  "rule": "BR-DE-2",
  "message": "The seller contact (BG-6) is required for XRECHNUNG.",
  "location": "invoice.seller.contact",
  "count": 1
}
```

| Field                   | Description                                                                                                                                |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `status`                | `passed`, `failed` or `unavailable`. `unavailable` means the validator couldn't be reached in time and the file was delivered unvalidated. |
| `level`                 | PDF/A level that was checked, `2b` or `3b`                                                                                                 |
| `profile`, `syntax`     | The e-invoice profile and syntax that were generated                                                                                       |
| `validator`, `scenario` | The validator, its version and the rule set it applied                                                                                     |
| `errors`, `warnings`    | Findings, each with `rule` (such as `BR-DE-15` or a PDF/A clause like `6.2.4.3-2`), `message`, `location` and `count`                      |

Each list holds at most 100 findings. Findings with the same rule and location are merged, and `count` says how often they occurred. For problems found in the invoice model, `location` is a JSON path such as `invoice.seller.contact.email`. For problems in the generated file, it is the validator's XPath or veraPDF's object path. The dashboard shows the same report on the render's page.

## Errors and warnings [#errors-and-warnings]

A failed e-invoice render isn't billed. Synchronous requests answer `422` with the error, and the report is in the render's `result`:

| Code                         | When                                                                                                                                                                                   | Fix                                                                                                                                     |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `einvoice_invalid`           | The invoice breaks a rule of the profile before any XML is written, for example a missing BT the profile requires, a VAT category without a rate or a `net_amount` that doesn't add up | `result.conformance.einvoice.errors` lists every problem with a JSON path into the invoice                                              |
| `einvoice_validation_failed` | The validator rejected the generated XML                                                                                                                                               | Read the rules in `result.conformance.einvoice.errors`; they are usually about content, such as a missing Leitweg-ID or an invalid code |
| `pdfa_conversion_failed`     | The PDF couldn't be made PDF/A conformant, or veraPDF found it non-compliant                                                                                                           | See [PDF/A](/docs/pdf-options#pdfa); fonts that aren't embedded are the usual cause                                                     |

Warnings don't fail the render:

| Code                              | Meaning                                                                                                                                                                         |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `einvoice_validation_warning`     | The validator accepted the XML with warnings; the message names the rules                                                                                                       |
| `einvoice_validation_unavailable` | The e-invoice validator didn't answer in time, so the XML is unvalidated                                                                                                        |
| `pdfa_validation_unavailable`     | veraPDF didn't answer in time, so the PDF/A file is unvalidated                                                                                                                 |
| `einvoice_skipped_plan`           | The template embeds an e-invoice [by default](#make-a-template-an-e-invoice-by-default), but the plan doesn't include e-invoicing, so the PDF has none                          |
| `invoice_totals_unavailable`      | The render embeds no e-invoice and its invoice couldn't be computed, so `render.invoice` is missing; the message names the first problem, such as a missing `invoice` in `data` |

See [Errors](/docs/errors) for every other code.

## Test mode [#test-mode]

Test renders are free and validated like live renders, so you can build and check your integration with a test key. Test PDFs carry the TEST watermark. It is drawn as vector outlines and stamped before the PDF/A conversion, so a test PDF is conformant too and the report describes the file you receive.

An XML file can't carry a watermark, so test XML carries a note instead. This applies to `POST /v1/einvoices` and to the XML embedded in a test PDF, the part that systems receiving the invoice read. The note is the first invoice note (BT-22):

```text
TESTRECHNUNG – keine gültige Rechnung. TEST INVOICE – not a valid invoice (created with a test API key).
```

* Every profile gets the note except `MINIMUM`, which has no field for it. A `MINIMUM` XML isn't an e-invoice on its own.
* `EXTENDED` also sets `TestIndicator` to `true`, the test flag that only its schema defines.
* Apart from the marker, the XML is the one a live key produces, and the validation report is the same.

Use a live key for invoices you send.

## Billed renders [#billed-renders]

| Operation                            | Billed renders                               |
| ------------------------------------ | -------------------------------------------- |
| E-invoice layer on a PDF render      | included (+0), only the PDF itself is billed |
| XML export with `POST /v1/einvoices` | 1                                            |
| Test renders and failed renders      | 0                                            |

A two-page Factur-X invoice is 1 render. E-invoicing needs the Growth plan or higher. See [Plans and limits](/docs/plans-and-limits#what-counts-as-a-render).

## Related pages [#related-pages]

* [PDF options](/docs/pdf-options#pdfa) for PDF/A without an e-invoice, and for converting existing PDFs
* [Renders](/docs/renders#the-render-object) for the render object and its `result`
* [Template language](/docs/template-language) for `format_currency` and the other filters
