How it works
- Your software sends
POST /api/v1/einvoiceswith a bearer token (see Getting a bearer token) and the invoice as JSON. - The server checks the data, calculates missing totals and maps every value to the fields of its invoice form (by default the form
zugferd). - The form is submitted through the normal form processing. It creates the EN 16931 XML (Cross Industry Invoice, ZUGFeRD / Factur-X profile EN 16931, written following the KoSIT rules used for XRechnung), embeds it into a PDF/A-3 and seals the PDF.
- The PDF goes back to your software (as Base64 in JSON, or as the file itself with
Accept: application/pdf) and, like the web form, by e-mail to the business owner of the form and to the e-mail addresses in the invoice (seller and buyer).
"action": "preview" stops after step 2: you receive the filled invoice form as PDF, nothing is sealed and no e-mail is sent. Use it to check a layout or your mapping.
Smallest request
Invoice number, seller, buyer and at least one line are required. The invoice date defaults to today, the currency to the form's preset (EUR on the sample form), and all totals are calculated.
curl -X POST https://forms.example.com/api/v1/einvoices \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -H "Accept: application/pdf" \
-o invoice.pdf -d '{
"invoice": { "number": "2026-0042", "taxPercent": 19 },
"seller": { "name": "Example Supplies GmbH", "street": "Musterstrasse 1", "postCode": "10115", "town": "Berlin", "country": "DE", "vatId": "DE123456789" },
"buyer": { "name": "Example Hotel AG", "street": "Seestrasse 1", "postCode": "12345", "town": "Musterstadt", "country": "DE" },
"items": [ { "name": "Consulting (hours)", "quantity": 4, "netPrice": 120 } ]
}'Complete example (XRechnung)
An invoice to a German public authority, with Leitweg-ID, contact data, delivery date and SEPA payment. Dates are ISO 8601 (yyyy-MM-dd; dd.MM.yyyy is accepted too), amounts are numbers with a dot as decimal separator.
{
"action": "submit",
"invoice": {
"number": "2026-0042", "date": "2026-10-11", "currency": "EUR",
"dueDate": "2026-10-25", "deliveryDate": "2026-10-09", "paymentTerms": "14 days net",
"buyerReference": "04011000-12345-34", "buyerOrderReference": "PO-7781",
"taxPercent": 19, "taxCategory": "S", "note": "Thank you for your order."
},
"seller": { "name": "Example Supplies GmbH", "contactName": "Erika Muster", "street": "Musterstrasse 1",
"postCode": "10115", "town": "Berlin", "country": "DE", "phone": "+49 30 1234567",
"email": "billing@example.com", "vatId": "DE123456789" },
"buyer": { "name": "City of Musterstadt", "contactName": "Max Beispiel", "street": "Rathausplatz 1",
"postCode": "12345", "town": "Musterstadt", "country": "DE", "email": "invoices@example.org" },
"payment": { "iban": "DE02120300000000202051", "bic": "BYLADEM1001", "accountName": "Example Supplies GmbH" },
"items": [
{ "name": "Consulting (hours)", "quantity": 4, "netPrice": 120, "sellerAssignedId": "C-100" },
{ "name": "Travel flat rate", "netPrice": 80 }
]
}Field reference
Every JSON field, the EN 16931 business term (BT) it fills and the field name on the invoice form. Required fields are marked.
| JSON | Meaning | EN 16931 | Form field |
|---|---|---|---|
invoice.number required | Invoice number | BT-1 | InvoiceID |
invoice.date | Invoice date (default: today) | BT-2 | date |
invoice.currency | Currency, ISO 4217 (EUR, CHF, …) | BT-5 | InvoiceCurrencyCode |
invoice.dueDate | Payment due date | BT-9 | DueDate |
invoice.buyerReference | Buyer reference; for XRechnung the Leitweg-ID | BT-10 | BuyerReference |
invoice.buyerOrderReference | Purchase order number of the buyer | BT-13 | BuyerOrderReference |
invoice.paymentTerms | Payment terms as text | BT-20 | PaymentTerms |
invoice.note | Invoice note | BT-22 | InvoiceNote |
invoice.deliveryDate | Actual delivery date | BT-72 | DeliveryDate |
invoice.paymentReference | Remittance information (only if the form has the field) | BT-83 | PaymentReference |
invoice.taxPercent | VAT rate in percent for the whole invoice | BT-152 / BT-119 | TAXPercent |
invoice.taxCategory | VAT category: S, Z, E, AE or G (see below) | BT-151 / BT-118 | TaxCategory |
invoice.taxExemptionReason | Reason for E, AE, G | BT-120 | TaxExemptionReason |
seller.name required | Seller name | BT-27 | SellerName |
seller.street, street2 | Address lines | BT-35, BT-36 | SellerStreet1, SellerStreet2 |
seller.postCode, town, country | Post code, town, country (ISO 3166-1, e.g. DE) | BT-38, BT-37, BT-40 | SellerPostCode, SellerTown, SellerCountry |
seller.vatId | VAT identification number | BT-31 | SellerVAT |
seller.contactName, phone | Contact person and phone | BT-41, BT-42 | SellerPersonName, SellerPhone |
seller.email | Seller e-mail as electronic address | BT-34 | SellerEmail |
buyer.name required | Buyer name | BT-44 | BuyerName |
buyer.street, street2, postCode, town, country | Buyer address | BT-50 to BT-55 | BuyerStreet1 … BuyerCountry |
buyer.vatId | Buyer VAT identification number | BT-48 | BuyerVAT |
buyer.contactName, phone | Contact at the buyer | BT-56, BT-57 | BuyerPersonName, BuyerPhone |
buyer.email | Buyer e-mail as electronic address | BT-49 | BuyerEmail |
payment.iban, bic, accountName | Bank account for the SEPA credit transfer | BT-84, BT-86, BT-85 | IBAN, BIC, AccountName |
items[].name required | Item name | BT-153 | ItemName1, ItemName2, … |
items[].quantity | Quantity (default 1, unit C62 = piece) | BT-129 | BilledQuantity1, … |
items[].netPrice required | Net unit price | BT-146 | NetPriceProductTradePrice1, … |
items[].lineTotal | Line net amount (default: quantity × net price) | BT-131 | LineTotalAmount1, … |
items[].sellerAssignedId | Seller's item number (only if the form has the field) | BT-155 | SellerAssignedID1, … |
totals.lineTotal, taxTotal, grandTotal | Totals; calculated when missing | BT-106, BT-110, BT-112 | LineTotalAmount, TaxTotalAmount, GrandTotalAmount |
Amounts, VAT and rounding
- Line amount = quantity × net price, rounded to 2 decimals (half away from zero), unless you send
lineTotal. - Sum of lines = sum of the line amounts; it is also the taxable amount and the total without VAT.
- VAT = sum of lines ×
taxPercent/ 100, rounded to 2 decimals. Total = sum of lines + VAT; the amount due equals the total. - Totals you send in
totalswin over the calculation, so you can pass the exact figures of your accounting system. - One VAT rate applies to the whole invoice. For invoices with several VAT rates, send one invoice per rate or ask us about the ZUGFeRD Pro SDK.
- Numbers:
1234.5or"1234.50". A comma is refused, so"12,50"can never become 1250.
VAT categories
Sstandard rate (default). With a rate of 0 it becomesZ.Zzero rated goods.Eexempt from VAT; withouttaxExemptionReasonthe reason "Steuerfreie Leistung" is used.AEreverse charge (exemption code VATEX-EU-AE); default reason "Steuerschuldnerschaft des Leistungsempfängers".Gexport outside the EU (VATEX-EU-G).
For E, AE and G the rate is set to 0.
Payment
With an iban the invoice carries a SEPA credit transfer (payment means code 58) with BIC and account name. Without an IBAN the payment means is "not defined" (code 1). Payment terms (paymentTerms) and due date (dueDate) are written in any case; without a due date the invoice date is used.
XRechnung checklist
Invoices to German public authorities must follow XRechnung. Send at least:
invoice.buyerReference= the Leitweg-ID of the authority (without it the server writes a generated reference, which an authority will reject).seller.emailandbuyer.email(electronic addresses),seller.contactName,seller.phone.seller.vatId, complete addresses with country codes,invoice.dueDateorpaymentTerms.- Payment data (
payment.iban) if the authority pays by transfer.
The XML is written following the KoSIT rules. Check your first invoices once with the KoSIT validator or a viewer of your choice.
The answer
201 Created with the submission ID, the PDF and the totals that were used:
HTTP/1.1 201 Created
{
"submissionId": "7f3c2a9e0d5b4e1fa2c8b6d4e9f01a23",
"formId": "zugferd",
"action": "submit",
"status": "submitted",
"pdf": { "fileName": "zugferd.pdf", "contentType": "application/pdf", "size": 152331,
"sha256": "…", "eInvoice": true, "data": "JVBERi0xLjc…" },
"totals": { "lineTotal": 560.00, "taxTotal": 106.40, "grandTotal": 666.40, "currency": "EUR" },
"notOnForm": [ "PaymentReference", "SellerAssignedID1", "SellerAssignedID2" ]
}
pdf.eInvoiceistruewhen the e-invoice XML is embedded in the PDF.notOnFormlists values the chosen form has no field for (they are not part of the invoice).- With
Accept: application/pdf(or"returnPdf": "binary") the answer is the PDF itself; the headerX-Submission-Idcarries the ID."returnPdf": "none"leaves out the PDF data. "action": "preview"answers 200 with the filled, unsent invoice.
Using your own invoice form
By default the server uses its invoice form (setting Form for e-invoices, preset zugferd). Send "formId" to use another one, for example an invoice form with your letterhead. The form must be released and needs fields with the names in the field reference above; lines are numbered from 1 (or from 0). The number of ItemName… fields is the maximum number of lines: the sample form has 8.
Design the form in the editor, name the fields as listed, release it, then check the mapping with "action": "preview". GET /api/v1/forms/{formId}/fields shows the field names of a form.
Limits and errors
Errors come as problem details (application/problem+json); see also the error list of the API.
| Status | Code | Meaning |
|---|---|---|
| 422 | validation_failed | A required value is missing (number, seller or buyer name, line name or price), a date or number cannot be read, or a value does not fit the form (e.g. unknown currency or country in a drop-down). |
| 422 | too_many_items | More lines than the form has room for. |
| 422 | not_an_invoice_form | The chosen form has no invoice line fields. |
| 403 | role_required | Creating invoices needs the role user or admin. |
| 404 / 409 | form_not_found, form_not_released | The invoice form does not exist for this token, or is not released. |
| 429 | rate_limited, daily_limit | Too many requests, or the daily cap of API submissions is reached. |
| 502 | form_processing_failed | The invoice could not be created or sent; nothing was delivered. |
Questions and answers
Which ZUGFeRD profile does the API create?
The EN 16931 profile (called COMFORT in ZUGFeRD 1), Cross Industry Invoice XML embedded in a PDF/A-3, identical to Factur-X. The XML is written following the KoSIT rules that XRechnung uses.
How do I create an XRechnung?
Send the Leitweg-ID of the public authority as invoice.buyerReference and fill in the e-mail addresses and contact data of seller and buyer. The API then creates an EN 16931 invoice that meets the XRechnung rules, embedded in the PDF.
Can I send the totals of my accounting system?
Yes. Values in totals (lineTotal, taxTotal, grandTotal) and items[].lineTotal win over the calculation.
Is the invoice e-mailed automatically?
Yes, like the invoice web form: to the business owner of the form and to the e-mail addresses in the data. Use "action": "preview" to get the PDF without sending.
Can an invoice have several VAT rates?
Not through this endpoint: one rate applies to the whole invoice. Send one invoice per rate, or use the ZUGFeRD Pro SDK for complex invoices.
Create your first e-invoice
Ask for test credentials and try the e-invoice API on our online server.
Last updated: