Documentation & API

A lean REST API for EN 16931 e-invoices: create, validate, embed, extract. JSON in, result out. The same engine as the web interface.

Standards & compliance

StandardZUGFeRD 2.x / Factur-X 1.0, selectable via invoice.profile (default: en16931/Comfort)
SyntaxUN/CEFACT CII (Cross Industry Invoice, D16B)
ContainerPDF/A-3 with embedded factur-x.xml (AFRelationship: Alternative); only with profile xrechnung pure XML instead of PDF
Validated withofficial KoSIT validator, accepts EN 16931 & XRechnung (CII + UBL)
Document typesInvoice (380), Partial invoice (326), Advance payment invoice (386), Credit note (381), Correction/cancellation invoice (384), Self-billed invoice (389), Construction instalment invoice (875), Construction partial final invoice (876), Construction final invoice (877)

Every invoice created is therefore a valid ZUGFeRD, Factur-X and EN 16931 file at the same time.

invoice.profileWhen to use
en16931 (default)Comfort: suits practically every B2B invoice and is accepted by almost any software
xrechnungMandatory for invoices to German public authorities (B2G): pure XML instead of PDF; invoice.buyer_reference, seller.email/phone, buyer.email and invoice.payment.iban are also required
basicleaner variant of Comfort with line items, but without the optional additional fields
basicwllike basic, but without line items in the XML: for flat-rate or subscription invoices
minimumpure booking message (no line items, no contact details); many systems and authorities do not accept this as a full invoice
extendedlargest feature set: including lines[].surcharge (line surcharge) and invoice.extra_charge_* (document-level charge, e.g. shipping)

Quick start

1
Create an API key in the dashboard.
2
Base URL: https://sichere-erechnung.de
3
Create your first invoice:
curl -X POST https://sichere-erechnung.de/v1/generate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  --data-binary @invoice.json -o invoice.pdf

Authentication

Every call needs your API key in the header, as a bearer token or X-API-Key:

Authorization: Bearer YOUR_API_KEY

You manage keys in the dashboard (create/revoke). The key is stored only as a hash and is visible only once, when it is created.

Multiple companies (X-Company)

If you run several sender companies in one company group, every API key belongs to one company: without further details, everything is created in that company (number range, sender, archive, receivables). You address another company in the group with the header X-Company, using the company's short code, account ID or VAT ID as the value. With a JSON body, the top-level field company also works; for /v1/automap only next to data, because a JSON body without data is itself the raw text there. For XML and file uploads only the header applies.

X-Company: LTD

Company management in the web interface

Under Companies owners and team admins create further companies (short code, name, country, legal form; for companies based outside Germany the legal form is a free-text field), deactivate them or reactivate them. Each company has its own master data, number range, customers, catalogue, archive, dunning and mailbox; login, team, plan, credits and AI switch apply to the whole group. With two or more companies you switch the active company in the top bar; the group view shows open receivables per company and currency and provides the full export with one folder per company. In the file import, the column absender (sender) selects the company per invoice; incoming invoices from the mailbox land in the addressed company.

Price: The first company is included. Each additional one costs 390 credits per month from the group's shared balance, charged at the start of the month and when created or reactivated. If the balance is insufficient, the company stays active and all documents remain accessible, but no new invoices can be created from it (422 with a note) until credits are topped up. Deactivated companies cost nothing from the following month.

Activation: We enable additional companies per account. Write to us at office@fhcp.de.

Endpoints

MethodPathFunction
POST/v1/ciiJSON invoice → EN 16931 CII XML (for your own PDF layouts)
POST/v1/generateJSON invoice → finished ZUGFeRD PDF/A-3 (PDF + XML), including a payment QR code (GiroCode) for EUR + IBAN and a “Pay now” payment page: the public payment URL is printed on the invoice and returned in the header X-Pay-Url (can be switched off per profile). The invoice also appears in your invoice history and open items (dunning available). Optional invoice.payee.name for a different payee (BG-10, e.g. assignment/factoring). With "seal": true you also get an authenticity seal: QR code in the PDF, verify_url in the header X-Seal-Verify-Url. Also works with XML instead of JSON: Content-Type: application/xml and a finished EN 16931 invoice in the body, as CII (ZUGFeRD/Factur-X/XRechnung) or as XRechnung UBL (Invoice and CreditNote; we convert UBL to CII), options then as ?seal=1&template=1; anything that could not be taken over during import is listed in the header X-Import-Warnings. Third-party XML without EN 16931 syntax is rejected with 422; use /v1/import?ai=1 for that
POST/v1/embedYour own invoice PDF (PDF/A-1b) + JSON → Factur-X PDF/A-3, your layout is kept. Instead of invoice you can also send invoice_xml (EN 16931 XML as text, CII or UBL) or the multipart field xml (file). Optional "seal": true → verify_url in the header (no layout change)
POST/v1/importImport from Excel, CSV, ODS or XML (CII or XRechnung UBL): multipart field file (extension in the file name) or the file as raw body. ?mode=preview (default, free of charge): recognised invoices as JSON with totals and errors per invoice, nothing is created. Spreadsheets contain one row per line item (columns: see Import); a single Excel invoice is read as a whole. If the structure cannot be recognised (or an XML is neither CII nor UBL, response then with kind: fremd), the preview responds with needs_ai: true and the missing fields; with ?ai=1 the AI does the mapping (AI import rate, beta for third-party XML). ?mode=generate: one ZUGFeRD PDF/A-3 per invoice at the generate rate; for Excel or LibreOffice (.xlsx, .xls, .ods) plus a conversion surcharge per file (2 credits, also with a plan; not charged for CSV and XML). Optional ?template=1 (your own Word template, same surcharge as when creating) and ?seal=1 (authenticity seal per invoice with IBAN; your plan's free quota applies). The preview lists everything in advance in costs (base, convert, tpl, seal, total); invoices without template or seal are reported in the header X-Import-Notes. Response as ZIP; for exactly one invoice the PDF directly (header X-Invoice-Number). Validation errors → 422 with a list, nothing is created. Large files run in the background: 202 with job_id
GET/v1/import/{id}Status of a background run: status (queued, running, done, failed), total/done/failed, error list. Free of charge, also with a read-only key
GET/v1/import/{id}/zipResult ZIP of a finished run, kept for 7 days. Free of charge
POST/v1/automapAI invoice creation from free text: unstructured data (text, email, spreadsheet row) raw in the body or as {"data": "…"} → recognised invoice JSON in the full invoice schema (references, Leitweg ID, service period, early payment discount, allowance/charge, payee, delivery address, line details), strictly validated against EN 16931 and completed with the sender (your profile), known recipients (customer records) and prices (catalogue, items with an exactly matching name). Default: JSON response (mapped + normalized). With ?generate=1 or {"generate": true} you get the finished ZUGFeRD PDF directly in one call (optional "seal"/"template" as with /v1/generate). With ?catalog=1 or {"catalog": true} the AI sees your entire catalogue while recognising and finds the right catalogue price even when the wording differs (e.g. “consulting” for “initial consulting”), instead of only matching exact names afterwards; this costs three times as much. Costs the AI import rate, with generate plus the generate rate, with catalog three times the AI import rate instead. Customer API key only (uses profile, customer records and catalogue)
POST/v1/validateXML or PDF → KoSIT validation result (compliant yes/no + messages). A non-compliant invoice also returns 200: the verdict is in valid, the violations in messages. So evaluate valid, not the status code. Valid means the validator recommends acceptance; warnings and information are listed separately in hints. 503 only if the validator is currently not running (then free of charge)
POST/v1/checkXML or PDF → incoming invoice check: KoSIT + IBAN fraud check (supplier history, bank change, IBAN country, detection of collection accounts shared across suppliers, your block list) + duplicate + consistency between visible layer and data layer (visible PDF IBAN vs. XML) + external sources (issuer VAT ID via EU VIES, recipient bank via Bundesbank bank codes, EU sanctions list, address) + amount due (BT-115, invoice.prepaid_total and invoice.due_payable, see Advance payment) → risk report (green/yellow/red), saved in the check history
POST/v1/sealInvoice (JSON as for /generate OR core fields) → authenticity seal (Ed25519), returns verify_url
POST/v1/extractZUGFeRD PDF → embedded XML + core fields
POST/v1/validate/fraudXML or PDF → KoSIT validation plus IBAN fraud check against your supplier history (risk unknown/low/high). Smaller variant of /v1/check without duplicate check and without saving to the check history
POST/v1/diffCompare two invoices (multipart original + corrected) → line and header diff as JSON, with ?format=html as an embeddable Git-style diff (red/green)
GET/v1/receivablesOpen items as JSON (amounts in cents). Query: status=open|overdue|paid|all, since=<ISO date>, page, per_page; with number=<invoice no.> a single item including payments[]. Per entry: amount due, claim after offsetting, open and overpaid amount, closed_reason and the links to advance, final and cancellation invoices (see Advance payment). Empty list = HTTP 200. Free of charge
POST/v1/receivables/paymentRecord an incoming payment (partial, full or overpayment): {"number": "R-1", "amount_cents": 11900, "value_date": "2026-09-24", "note": "Bank statement 42"}. Without value_date today applies; a date in the future or an amount_cents that is not a whole number returns 422. An overpayment is recorded and reported as overpaid_cents; cancellation and correction documents do not accept payments (409). Free of charge
POST/v1/receivables/paidRecord the open balance as settled: {"number": "R-1", "value_date": "2026-09-24"}. Nothing open: 409. Free of charge
POST/v1/receivables/refundRecord the refund of an overpayment: {"number": "R-1", "amount_cents": 1000, "value_date": "2026-09-24", "note": "transferred back"}, up to overpaid_cents at most, otherwise 409. Free of charge
GET/v1/partnersBusiness partner master data as JSON (customers, suppliers, prospects; roles are multi-valued, one partner can have several). Query: q=<search across name/email/VAT ID>, role=customer|supplier|prospect, country=<ISO-2>, archived=1, page, per_page (max. 500); with no=<partner number> exactly one partner (archived ones included, otherwise your system would consider the number free). Empty list = HTTP 200. Free of charge
POST/v1/partnersCreate or update a partner. The key is your own partner_no (not our internal ID), compared case-insensitively and ignoring leading and trailing spaces (k-1001 updates K-1001, the first spelling is kept), the same rule as in the CSV import and for kundennummer in the invoice import: {"partner_no": "K-1001", "name": "Muster GmbH", "city": "Berlin", "country": "DE", "vat_id": "DE123456789", "is_customer": true}. Further fields: street, zip, tax_number, email, phone, is_supplier, is_prospect, legal_status (business|consumer|public), sepa_mandate, sepa_iban, the invoice defaults payment_terms_days, skonto_days, skonto_percent, tax_scheme_override, currency, invoice_profile, leitweg_id, billing_street/zip/city/country/email, credit_limit_cents, delivery_terms (delivery terms, e.g. Incoterms) and dunning per partner: dunning_mode (auto|manual|off), dunning_tone (freundlich|neutral|bestimmt, i.e. friendly, neutral, firm), dunning_fees ({"2": 250, "3": 250}, cents per dunning level), dunning_pauschale, dunning_damages, dunning_stage_days ({"1": 3, "2": 10, "3": 21}, days after the due date), dunning_deadline_days, dunning_texts ({"1": "…"}), dunning_texts_en (English version for invoices in English, empty = English default text); null means “same as account”, the response shows them under dunning, invalid values return 422. Omitted fields remain unchanged (so saving from your master data module does not reset any role). 201 when created, 200 when updated. If several partners share the same number (e.g. assigned twice in the interface), lookup with no=, update and archive respond with 409, list the partners in error.details and change nothing until you give one of them a different number or merge the duplicates. Free of charge
POST/v1/partners/archiveArchive a partner: {"no": "K-1001"}. Nothing is ever deleted, because documents are attached to partners (GoBD, the German rules for digital bookkeeping records). Free of charge
GET/v1/companiesCompanies in your company group as JSON: {"companies": [{"id", "code", "name", "country", "vat_id"}], "group_scope": true}. Without group permission only the key's own company (see Multiple companies). Free of charge, also with a read-only key
POST/v1/embed/tokenWhite label: mint a short-lived embed token (form in your own design via iframe, end customer without an account) → token + embed_url. Free of charge
GET/v1Machine-readable endpoint overview including current credit costs (no key needed)
POST/v1/dunningDunning letter PDF for one of your own invoices (receivables management): {"number": "R-1", "level": 1..3 optional} → PDF with a statement of the claim (default interest under §§ 286/288 of the German Civil Code (BGB), €40 flat fee, dunning costs); the dunning level is logged. Tone, fees and deadlines come from the effective settings (account, business partner, invoice, see Dunning); if dunning is set to off there, the call responds with 422. Headers X-Dunning-Level / X-Dunning-Total-Cents
GET/verify/{token}Public verification of a seal (no key, no credit)
GET/healthReadiness (no key, no credit)

Example input (applies to /v1/cii and /v1/generate):

{
  "invoice": { "number": "2026-0001", "issue_date": "2026-06-29",
                "currency": "EUR", "payment": { "iban": "DE02..." } },
  "seller": { "name": "Ihre GmbH", "street": "Weg 1", "zip": "10115",
               "city": "Berlin", "country": "DE", "vat_id": "DE123456789" },
  "buyer":  { "name": "Kunde AG", "street": "Allee 2", "zip": "80331",
               "city": "München", "country": "DE", "vat_id": "DE987654321",
               "contact_name": "Erika Muster" },
  "lines": [ { "name": "Consulting", "qty": 10, "unit": "Hours",
               "unit_price": 120.00, "vat_rate": 19 } ]
}

?validate=1 additionally validates against KoSIT before delivery. Mandatory fields follow EN 16931 (full address of seller and buyer, VAT ID or tax number, at least one line item).

Idempotency (recommended for retries): POST /v1/generate accepts the header Idempotency-Key: <your ID>. A repeated call with the same key and the same body returns the stored response again (header X-Idempotent-Replay: true) without creating the invoice a second time or deducting credits again. Valid for 24 hours; the same key with a different body → 409.

Custom labels in the Word template: "invoice": {"label_overrides": {"de": {"label_payment_terms": "Zahlungsbedingungen"}, "en": {"label_payment_terms": "Terms of payment"}}} replaces the texts of the ${label_*} placeholders in the language of the document (at most 80 characters; without it, the custom texts stored in your profile apply, otherwise the default). Applies to the Word template only, not to the standard layout or the XML. ${label_item_discount} appears only on lines with a discount; ${item_discount_text} shows the discount with its label and without a sign (“Discount 10.00%”). ${label_item_code} labels ${item_code} (default “Item code”), ${label_order_confirmation} is the short label for ${seller_order_ref} (default “Confirmation”).

Further options for /v1/generate: "seal": true (authenticity seal, see below) and "template": true (rendering with the Word template / corporate design stored in your profile, surcharge as per the price list below). Response headers: X-Credits-Cost (credits used), X-Pay-Url (public “Pay now” page), X-Seal-Verify-Url (with seal), X-Invoice-Template: used (template used) or X-Template-Warning with the reason if the PDF was created in the standard layout. A credit note and a document with a negative amount only run through a template that has the placeholder ${payment_text} (all templates created from 24 Sep 2026), otherwise in the standard layout, and the surcharge is then refunded. Likewise, advance payment invoices need ${service_text}, documents with an amount already paid need ${due_total} and documents with a note need ${advance_note} (see Advance payment). Foreign currency invoices need ${tax_total_tax_currency} (VAT in EUR); there are also ${tax_currency}, ${exchange_rate_text} (rate with source and date) and the block ${fx_block}…${/fx_block}, which is dropped for EUR.

Invoices with an early payment discount need ${skonto_text}, ${payment_terms} or ${payment_text}: without ${skonto_text}, the discount sentence appears on its own line below ${payment_terms}, otherwise before ${payment_text}; without all three, the invoice is created in the standard layout (the discount is a mandatory detail under Section 14 (4) no. 7 UStG). ${item_net} is the line amount after the line discount and charge; ${item_gross} and ${item_discount} are also available.

AI invoice from free text (one call, free text → finished PDF; uses your profile, customer records and catalogue):

curl -X POST "https://sichere-erechnung.de/v1/automap?generate=1" \
  -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"data": "Invoice 2026-0007 to Muster AG, Munich: 10 hrs consulting at 120 EUR, 19% VAT", "seal": true}' \
  -o invoice.pdf

# Without generate: only the recognised JSON is returned (mapped + normalized), for prefilling your own forms:
curl -X POST "https://sichere-erechnung.de/v1/automap" -H "Authorization: Bearer YOUR_API_KEY" \
  -d 'Customer Muster AG, Munich; 10 hrs consulting at 120 EUR each; invoice no. 2026-0007'

If the AI recognises too little for a valid invoice, generate returns no PDF but 422 with the missing fields (then only the AI import rate is charged, not the generate rate).

Further fields & cancellation invoice

seller.tax_numberTax number as an alternative to seller.vat_id (VAT ID)
buyer.vat_idBuyer's VAT ID (BT-48): required with invoice.tax_scheme: reverse_charge or innergemeinschaftlich (see below)
invoice.due_dateDue date (BT-9); if omitted, the invoice states “payable immediately without deduction”
invoice.delivery_dateDate of supply (BT-72); default = issue_date, not shown if period_start/period_end is set. Advance payment and construction instalment invoices (386, 875) carry no date of supply in the XML, but the expected date instead (invoice.expected_delivery_from, see Advance payment)
buyer.iban / buyer.bicCustomer's bank details. For a credit note (invoice.type_code 381) this is the refund account (BT-84); an invoice.payment.iban sent along is not used for 381. Without a customer IBAN the credit note gets payment code 97 (offsetting) with an explanatory note, no GiroCode and no payment link. Credit notes do not appear in open items, the receivables API, the cockpit or dunning; revenue totals deduct them. In the import: column kunde_iban, empty = taken from the partner record.
invoice.buyer_referenceLeitweg ID (BT-10, routing ID for German public authorities): required with profile xrechnung, otherwise optional
lines[].netexplicit net line amount instead of unit_price×qty (overrides the calculation, e.g. for odd flat rates)
lines[].surchargeLine surcharge (BG-28, absolute amount), counterpart to discount; only useful with profile extended
invoice.extra_charge_amount / _reason / _vat_ratedocument-level charge (BG-21, e.g. shipping costs) with its own tax rate, independent of the line items; _reason is mandatory as soon as _amount > 0; only useful with profile extended
seller.rechtsformLegal form (e.g. “GmbH”), appended to the company name instead of replacing it
seller.register_court / register_numberRegistry court + registration number (BT-30, e.g. “HRB 12345”), mandatory on business letters for GmbH/UG/AG/e. K. and others under German company law (§ 35a GmbHG, § 37a HGB, § 80 AktG)
seller.managing_directorsManaging directors / board as free text (BT-33, together with the registry court)
seller.trading_nameTrading name, if different from the company name (BT-28)
seller.website / seller.logooptional branding for the PDF layout; logo only as an embedded image (data:image/png;base64,…, likewise jpeg, gif, webp); a file path is discarded
invoice.header_textIntroductory text (BT-22)
invoice.footerFooter (e.g. managing director, commercial register number, tax notes)
invoice.referenceContract or customer reference (BT-12)
invoice.payment_termsPayment terms as text (BT-20, at most 2,000 characters): placed first in the payment terms of the XML, early payment discount details follow unchanged; without a due date they replace the standard sentence “payable immediately”. Without an entry, the payment terms from your profile apply in the document language (invoice.language, English text for en, otherwise the German one). Shown in the standard layout and in the Word template as ${payment_terms}
invoice.skonto_pdf_hidetrue: the early payment discount (skonto_days/skonto_percent) is only included as structured data in the XML (BT-20), the PDF (standard layout and Word template) does not add its own discount sentence; for payment terms that already state the discount. Without a value, the setting in your profile applies. If invoice.payment_terms does not clearly state the discount rate, you get a notice (the discount is a mandatory detail, section 14 (4) no. 7 UStG)
invoice.delivery_termsDelivery terms, e.g. Incoterms "FCA Clausthal-Zellerfeld (Incoterms 2020)" (at most 300 characters). EN 16931 has no field of its own for them: in the XML as a note (BT-22) with subject AAR (terms of delivery), in the PDF in the header and in the Word template as ${delivery_terms}. Default per business partner via delivery_terms in /v1/partners (applies in the invoice form and in the import)
invoice.our_reference“Our reference”, e.g. the initials of the person in charge (at most 60 characters); only in the PDF and in the Word template (${our_reference}), not in the XML
invoice.payment.{iban,bic,bank}Bank details; bic (BT-86, 8 or 11 characters) is written to the XML and, if the IBAN is the one in your profile, completed from the profile
invoice.payment.bank_accountOptional: one of the company’s bank accounts stored in the profile, by ID or name (e.g. "USD account"). Fills empty bank, iban and bic fields; values you send take precedence. Unknown account: 422
invoice.payment.referencePayment reference (BT-83); if omitted, the invoice number, so that incoming payment and invoice can be matched during bank reconciliation
invoice.allowance (old name: skonto)Allowance: unconditional, document-level deduction on the whole invoice (reduces the invoice amount and the tax base immediately). invoice.skonto is still accepted as a deprecated alias (unchanged behaviour); it is not a real early payment discount (Skonto) but this allowance; the name was misleading until 17 Sep 2026.
invoice.allowance_percent / allowance_reasonAllowance as a percentage of the net total of the line items (instead of allowance, not both). The amount is rounded half up to the cent and appears in the XML with percentage (BT-94) and base (BT-93); allowance_reason is the reason (BT-97, without one “Nachlass”, with document language en “Allowance”). With several VAT rates, each allowance is split proportionally. In the Word template: ${discount_amount}, ${discount_reason} and ${discount_percent} (percentage, empty for an amount).
invoice.allowance2 / allowance2_percent / allowance2_reasonSecond allowance on the same invoice, as an amount or a percentage, with its own reason; a separate allowance (BG-20) in the XML and a separate line on the document. In the Word template: ${discount2_amount}, ${discount2_reason} and ${discount2_percent}, empty without a second allowance.
invoice.skonto_days / skonto_percent (optional second tier: skonto2_days/skonto2_percent)Real early payment discount (Skonto) (BT-20): a conditional payment incentive: the invoice keeps the full amount, the totals do NOT change. Requires invoice.due_date. Days 1 to 90, percent 0.01 to 20. Appears as text in the payment terms plus the XRechnung syntax #SKONTO#TAGE=n#PROZENT=n.nn# (BR-DE-18, applies to all profiles) and on the PDF as a separate note with date and amount.
invoice.tax_currency / exchange_rateForeign currency (§ 14(4) no. 8 of the German VAT Act, UStG): if an issuer based in Germany invoices in a currency other than tax_currency (default EUR), the VAT must also be stated in tax_currency (BT-6/BT-111). This requires exchange_rate (1 invoice.currency = x tax_currency). If it is missing and the tax currency is EUR, the API inserts the official ECB rate for the date of supply (otherwise the end of the billing period, otherwise the invoice date) according to the rate type in your profile, daily rate or monthly average (= VAT conversion rate of the Federal Ministry of Finance under § 16(6) UStG), and reports this in X-Invoice-Hints. A rate you send always takes precedence. If no rate can be determined (currency not quoted by the ECB, source unavailable), the API responds with 422 and asks for the rate.
invoice.exchange_rate_source / exchange_rate_dateOrigin of the rate for audit purposes (GoBD), optional: ezb (ECB reference rate of one day), ezb_monat (monthly average of the ECB reference rates, equal to the VAT conversion rate of the Federal Ministry of Finance) or manuell, plus the rate date (YYYY-MM-DD, for the monthly average the first day of the month). Filled when the rate is set automatically; shown on the PDF in the rate line, not in the XML.
invoice.project_ref / project_nameProject number (BT-11) and optional project name; only in the XML with en16931/xrechnung/extended
invoice.seller_order_refYour sales order / order confirmation number (BT-14); counterpart to the buyer's purchase order number order_ref; en16931 and above only
seller.street2 / buyer.street2Additional address line (BT-36/BT-51, e.g. building, PO box)
seller.state / buyer.stateState/region (BT-39/BT-54)
lines[].item_id / lines[].buyer_item_idSeller's item number (BT-155) or buyer's item number (BT-156); en16931 and above only
lines[].gtinGTIN/EAN (BT-157, 8/12/13/14 digits, check digit is validated), in the XML as GlobalID schemeID="0160"
lines[].list_price / lines[].price_discountList price (BT-148) and price discount per unit (BT-147). unit_price must then equal list price minus discount or may be omitted (it is calculated); a contradiction is rejected with 422, because EN 16931 does not check this calculation itself
lines[].price_base_qtyPrice base quantity (BT-149), e.g. 100 for “price per 100 pieces”; line amount = quantity × price ÷ base quantity
lines[].origin_countryCountry of origin of the goods (BT-159, ISO code); en16931 and above only
lines[].noteLine note (BT-127), also shown on the PDF
lines[].unitUnit of measure (BT-130) as a UN/ECE Rec. 20 code or German word; the list of known codes is below
lines[].discount / lines[].discount_percentDiscount per line item (absolute amount or percent; EN 16931 line allowance)
invoice.tax_schemestandard · kleinunternehmer (small business, § 19 of the German VAT Act, UStG) · reverse_charge (§ 13b UStG, buyer VAT ID required) · innergemeinschaftlich (intra-community supply, buyer VAT ID required) · steuerfrei (tax-exempt, § 4 UStG, reason in invoice.tax_exemption_reason) · ausfuhr (export, category G, § 4 no. 1a UStG, export to a non-EU country, seller VAT ID required) · nullsatz (zero rate, category Z, 0 % VAT, seller VAT ID or tax number required) · nicht_steuerbar (not taxable, category O, place of supply abroad under § 3a UStG, cannot be combined with profile xrechnung, BR-DE-14 vs. BR-O-05, see the error code note below).
Important: For every scheme other than standard, lines[].vat_rate (and invoice.extra_charge_vat_rate) is ignored and set to 0, in XML, PDF and totals. A rate sent along, such as 19, changes nothing and does not cause an error.
May be omitted: Without invoice.tax_scheme the default stored in your profile applies (standard if nothing is set there). As a small business you therefore set the scheme once in your profile instead of sending it with every call; a value sent along, including "standard", always takes precedence.
Foreign invoices (since 2026-10-01): A seller in Germany with standard and at least one line at 0 % to a buyer in another country is rejected with 422, because otherwise category Z (zero rated) would be created silently. Please send innergemeinschaftlich, reverse_charge, ausfuhr or nicht_steuerbar; for a genuine zero rate send nullsatz explicitly.
invoice.period_start/period_endService period from/to (BG-14), shown instead of the date of supply; for 386 and 875 it becomes the expected service period
invoice.order_ref / delivery_note / accounting_refBuyer's purchase order number (BT-13) · delivery note (BT-16) · account assignment / cost centre (BT-19)
invoice.prepaid / roundingamount already paid without an advance payment invoice (BT-113) and rounding (BT-114); the amount due (BT-115) is reduced accordingly. Deduct advance payment invoices with invoice.deductions (final invoice)
invoice.extra_bankOptional: an additional bank account {"iban", "bic", "bank", "label"}, for example an account for customers abroad. Without it, the company adds one itself if one of its bank accounts is set up for the customer country (profile, “Also show for customers in”). It appears in the PDF, in the XML as a second credit transfer (BG-17, EN 16931 allows 0..n: another ram:SpecifiedTradeSettlementPaymentMeans after the main account with the same TypeCode 58, IBAN BT-84, from EN 16931 also account name BT-85 from label and BIC BT-86; BASIC and BASIC WL only with the IBAN, MINIMUM without payment details), on the payment page with its own GiroCode and in the authenticity seal. Not for credit notes and direct debits. Invalid IBAN: 422. When an XML is read in, the second credit transfer becomes invoice.extra_bank
invoice.payment.methodtransfer (default) or direct_debit (SEPA direct debit; then payment.iban = customer account to be debited, plus payment.mandate BT-89 and payment.creditor_id BT-90)
invoice.attachments[]supporting documents (BG-24): {name, mime: "application/pdf", data: base64}, max. 5, 1 MB each, surcharge per attachment (default 1 credit)
invoice.payee.namedifferent payee (BG-10, e.g. assignment/factoring)
buyer.contact_name · buyer.email · buyer.phoneBuyer contact (BG-9): name (BT-56, shown as “Attn.” on the invoice), email (BT-58), phone (BT-57)
buyer.partner_noYour customer number for this customer (e.g. "K-1001", at most 64 characters). In the XML as the buyer identifier BT-46 (ram:BuyerTradeParty/ram:ID, not for MINIMUM), in the PDF in the header (“Customer number”), in the Word template as ${customer_no}. The invoice form prefills it from the business partner; follow-up processing uses it to assign the invoice to the business partner. In the import: column kundennummer
seller.contact_name · seller.email · seller.phoneSeller contact (BG-6): name (BT-41), email (BT-43), phone (BT-42)
invoice.type_code380 invoice · 326 partial invoice (completed partial service) · 386 advance payment invoice · 381 credit note · 384 cancellation invoice · 389 self-billing · 875 construction instalment invoice · 876 construction partial final invoice · 877 construction final invoice.
With XRechnung a 386 is written as 380 with the note “Anzahlungsrechnung:” (advance payment invoice; BR-DE-17 is only a warning), and a 384 requires invoice.reference_invoice (BR-DE-26, § 31(5) sentence 2 of the German VAT Implementing Ordinance, UStDV); the other profiles only give a hint. Advance payment, partial and final invoices: see below.
invoice.languageDocument language: de (default) or en. It applies to the PDF, the Word template and the fixed texts in the XML. With en these appear in English: the payment terms sentences including the early payment discount (Skonto) sentence (BT-20), the reason for the tax exemption (BT-120), the reasons for discount, surcharge and allowance (BT-139, BT-144, BT-97), the notes on advance payment, remainder invoice, further references and authenticity seal (BT-22), the seller legal notices (BT-33), the credit note sentence (BT-82) and the deduction lines of earlier advance payments. Codes (e.g. VATEX, #SKONTO# lines, units), names and your own texts (header and footer text, item descriptions, delivery terms, own exemption or allowance reason, own payment terms) stay unchanged. Without it or with de everything is German as before, the XML too. Any other value is rejected with 422. For business partners the language can be stored as invoice_language (/v1/partners), in the invoice import as the column sprache.
invoice.reference_invoiceReference to the original invoice (BT-25) as text, as a list or as an object {number, issue_date}; further references in invoice.references[], date in invoice.reference_issue_date (BT-26)

You create a cancellation invoice purely via the API: set type_code: "384", set reference_invoice to the original number and enter the line quantities or amounts as negative. Exactly the negated gross amount of one of your own documents counts as a cancellation and closes it in the open items; cancelling a final invoice reverses its deductions (Advance payment):

{
  "invoice": { "number": "STORNO-2026-0001", "issue_date": "2026-07-01",
               "type_code": "384", "reference_invoice": "2026-0001" },
  "seller": { … }, "buyer": { … },
  "lines": [ { "name": "Consulting", "qty": -10, "unit": "HUR",
               "unit_price": 120.00, "vat_rate": 19 } ]
}

Units of measure (BT-130)

Codes according to UN/ECE Recommendation 20 (BR-CL-23). You can send the code or the German word; unknown codes are passed through unchanged.

CodeMeaning
C62Units
H87Piece
EAEach
PCEPiece (PCE)
NARNumber of articles
PRPair
SETSet
LSLump sum
P1Percent
E48Service unit
E49Working day
NPRNumber of pairs
MINMinutes
HURHours
DAYDays
WEEWeeks
MONMonths
ANNYears
SECSeconds
QANQuarters
MGMMilligrams
GRMGrams
KGMKilograms
TNETonnes
MLTMillilitres
LTRLitres
HLTHectolitres
MTQCubic metres
CMQCubic centimetres
MMTMillimetres
CMTCentimetres
MTRMetres
KMTKilometres
MTKSquare metres
CMKSquare centimetres
MMKSquare millimetres
HARHectares
KWHKilowatt hours
MWHMegawatt hours
KWTKilowatts
WHRWatt hours
XPKPackage
XPPPiece, unpacked
XBXBox
XCTCarton
XPAPacket
XBGBag
XRORoll
XBOBottle
XCACan
XTUTube
XPXPallet
XCSCrate
XCRCase
XSASack
XCYCylinder
XBJBucket
KMHKilometres per hour
D64Calendar days
DZNDozen
KTKit

Authenticity seal & public verification

An authenticity seal cryptographically binds the core fields (issuer, number, date, amount, IBAN) to an Ed25519 signature. Your customer scans the QR code or opens the link and sees immediately: genuine, unchanged, correct IBAN. This protects your customers against forged “invoices from you” with someone else's bank details.

curl -X POST https://sichere-erechnung.de/v1/generate \
  -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"seal":true,"invoice":{…},"seller":{…},"buyer":{…},"lines":[…]}'

# Response: ZUGFeRD PDF with QR code   ·   Header: X-Seal-Verify-Url: https://sichere-erechnung.de/verify/AbC123…

Integrate into your software

Any language with HTTP will do: accounting software, ERP, online shop or your own tool. Two examples:

PHP
$ch = curl_init("https://sichere-erechnung.de/v1/cii");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => ["Authorization: Bearer $key", "Content-Type: application/json"],
  CURLOPT_POSTFIELDS => json_encode($invoice),
  CURLOPT_RETURNTRANSFER => true,
]);
$xml = curl_exec($ch); // finished EN 16931 CII XML
Python
import requests
r = requests.post("https://sichere-erechnung.de/v1/generate",
    headers={"Authorization": f"Bearer {key}"},
    json=invoice)
open("invoice.pdf", "wb").write(r.content)

Typical flow in accounting software or an ERP: on “finalise invoice”, send the invoice data to /v1/generate and store or send the returned ZUGFeRD PDF. Check incoming supplier invoices via /v1/validate before booking them.

Billing & limits

Error codes

CodeMeaning
401Missing or invalid API key
402No balance, top up credits
403Email not yet confirmed, read-only key used for a write call, X-Company without group permission, or company deactivated
422Input unusable, or no compliant invoice can be created from it (details in the response). Not the case “checked, result negative”, which comes as 200 with valid: false
429Rate limit reached

Protection & corrections

Incoming invoice check (POST /v1/check, 2 credits): the comprehensive check of a received invoice (PDF or XML) in one call, KoSIT validation + IBAN fraud check (supplier history) + duplicate check. The response is a uniform risk report with risk: green|yellow|red and a list of findings (each with type, level: red|yellow|info|green, message; info is a note without effect on the risk). In addition to KoSIT, IBAN history, block list, IBAN vault, factoring detection, collection account, IBAN country and duplicate, the check detects fraud patterns from your own data, without external queries: payment pressure (pressure, red: new or changed IBAN + due within ≤ 7 days + first invoice from the supplier or first in 90 days), amount plausibility (amount: ≥ 3 times the median of the last 12 months yellow, round thousands from €5,000 info, amounts within 2 % below the usual approval limits €1,000/5,000/10,000/25,000 yellow), duplicate invoice with a new number (duplicate_new_number, yellow: same supplier, amount and IBAN within 30 days, but a different invoice number; a duplicate with the same number is still reported as duplicate; both can occur at the same time and then refer to two different earlier invoices), PDF forensics (consistency, red: visible IBAN, amount, invoice number, due date or issuer contradict the XML; pdf_meta: created with a word processor or online editor info, edited afterwards yellow, creation date > 30 days from the invoice date yellow) and, for invoices received via the mailbox, the sender check (sender: lookalike of a known supplier domain red, new domain yellow, Reply-To ≠ From yellow, SPF/DKIM failure yellow; the first domain per supplier is stored as a reference). Multiple companies: The check knows all companies in your group. If the invoice is addressed to another of your companies, the report contains company: {"status": "sister", "name"} and the finding recipient (yellow, not payable here); then check it with the header X-Company in that company. If the recipient matches several of your companies, the report shows company: {"status": "open", "candidates": [{"name"}]} and the finding empfaenger_firma (yellow); the invoice is not payable until it is assigned. An API key with group permission also receives id and code (short code) for each company. The block list applies to all companies in the group. Accounts without further companies get no company field. Every check is saved in the check history (visible in the web interface under Checked invoices). There you can mark checked invoices for payment and export them as a SEPA bulk transfer (pain.001), which you upload to your online banking (no money flows through us, the recipient IBANs have already been checked). In the IBAN vault you approve or block trusted supplier bank details; a blocked IBAN triggers a red warning on every check. No plain-text IBAN is stored (HMAC hash).

External sources in the incoming invoice check (free of charge, no registration, each as its own findings entry): vies: VAT ID of the issuer against EU VIES (invalid = red; if VIES returns the company name, it is compared with the invoice, mismatch = yellow; Germany returns no name). bank: for German IBANs, the bank code against the Deutsche Bundesbank bank code file (bank code does not exist = red; institution with instant account opening such as N26/Solaris/Revolut = yellow with explanation). sanctions: issuer name against the consolidated EU financial sanctions list (exact match = red, close match = yellow; no matches on names shorter than 5 characters). address: issuer address (DE/AT) via our address service (not found, or postcode and city do not match = yellow). If a source is currently unreachable, you get level: info (“check not possible”); the check never aborts and info does not affect the overall risk. VIES responses are cached for 24 h; the worker keeps the Bundesbank and sanctions lists up to date.

Team & four-eyes approval: Under Team you invite further users to your account (roles Admin/Member; shared balance, shared data and check history). As soon as there are at least two people in the team, the four-eyes principle applies automatically in the IBAN vault: an approval is requested and must be confirmed by a second person. Billing, API keys and account deletion remain reserved for owners and admins.

VAT ID monitoring & alerts: Under VAT ID check we automatically and regularly monitor your customers' stored VAT IDs via EU VIES and notify you by alert (dashboard + email) as soon as a previously valid number becomes invalid, which matters for reverse charge and tax exemption.

Mailbox connection (automatic incoming invoice check): In your profile (mailbox section) you connect an email mailbox via IMAP (read-only access, password encrypted). We fetch incoming invoices from it automatically, check them as above including the email sender (domain history per supplier, lookalike domains, Reply-To, SPF/DKIM from the headers, without network queries) and warn you by alert if anything looks suspicious, without manual uploads.

IBAN fraud protection (POST /v1/validate/fraud, 3 credits): validates a received invoice against KoSIT and compares the issuer's bank details with your supplier history. The response contains fraud_risk: high|low|unknown + a warning message. No plain-text IBAN is stored (HMAC hash). Detects: different IBAN, invalid check digit, IBAN country ≠ company location.

Several IBANs in an incoming invoice (additional credit transfers, BG-17): every additional IBAN is checked like the first against the block list, the IBAN vault, the supplier history and mule accounts. A known additional IBAN is not a finding, a previously unknown one appears as a yellow note (no fraud suspicion), a blocked or invalid one as red. The response lists them under fraud.extra_ibans (iban masked, status, risk, message), the verdict on the first IBAN under fraud.iban_risk. Invoices with one IBAN are assessed as before.

Invoice diff (POST /v1/diff): upload the fields original + corrected (PDF/XML) → structured line diff as JSON or, with ?format=html, as an embeddable Git-style diff view (green = new/more expensive, red/struck through = old/removed), also usable in the white label iframe.

Receivables management: dunning per partner and per invoice

Dunning has three levels, each inheriting from the one above: account (settings under Open items), business partner (“Dunning” card in the partner record, via API the fields dunning_* on POST /v1/partners) and invoice (“Adjust dunning” on the open invoice). Eight fields per level: mode (auto = the service sends reminders, manual = by hand only, off = never, button and API also refuse), tone, dunning texts per level, dunning costs of the first and last dunning letter, €40 flat fee, damages for late payment, level deadlines (days after the due date for the payment reminder, 1st and 2nd dunning letter, default 3/10/21) and payment deadline in the letter (default 10 days). An empty field means “same as the level above”; the interface shows next to each field which level supplies the value. The automatic run respects the levels as well: a partner set to auto receives dunning letters even if the account is set to manual, and vice versa.

Import from Excel, CSV and XML

Three ways, one creation pipeline: under Create from file you upload a file and first see a preview (invoices, line items, totals, errors with sheet and row); only then are the invoices created. POST /v1/import accepts the same files. Supported are Excel (.xlsx, .xls), LibreOffice (.ods), CSV (separator semicolon, comma or tab) and EN 16931 XML (ZUGFeRD/Factur-X CII and XRechnung in both syntaxes, CII and UBL). Excel files are read in an isolated container, formulas arrive calculated, macros are never executed.

Many invoices from one spreadsheet: one row per line item; rows with the same invoice number form one invoice. Header fields (recipient, payment, delivery address) may appear in every row; the first row per invoice counts. The header row may also sit below title rows. Mandatory columns are rechnungsnummer (invoice number), kunde (customer), position (item) and einzelpreis (unit price) or netto (net). The sender always comes from your profile. Download the template with all columns.

A whole Excel invoice: invoices you have written in Excel so far (letterhead, recipient block, item table) are recognised and taken over into the invoice form. If the layout is ambiguous, the interface offers AI mapping (opt-in, costs as for the AI import).

XML: an existing e-invoice (for example from another tool) is read back completely into the invoice schema, including cancellation reference, Leitweg ID, payment terms, early payment discount, delivery address and attachments. Anything that does not fit is reported as a note. UBL becomes CII in the process, which is permitted for XRechnung recipients. Beta Third-party XML (e.g. an ERP export without EN 16931 syntax) is not read deterministically; on request the AI maps the fields (opt-in, costs as for the AI import; check the result in the form).

All columns of the spreadsheet import (102)

Upper and lower case, umlauts and punctuation in the column name do not matter (“Rechnungs-Nr.” is recognised). Numbers in German (1.234,56) or English (1234.56) format, dates as DD.MM.YYYY or YYYY-MM-DD. einzelpreis or netto: one of the two is required. Column names are German, as listed below.

Columnalso recognised as
rechnungsnummer Requiredrechnungs-nr., rechnungsnr, rechnung nr, re-nr, nummer, belegnummer
belegartdokumentart, typ, type code, invoice type, document type
datumrechnungsdatum, belegdatum, date, invoice date, issue date
faelligfällig, faelligkeit, faelligkeitsdatum, zahlbar bis, due, due date
zahlungsziel_tagezahlungsziel, zahlungsfrist, zahlungsziel tage, payment term days, payment terms, net days
leistungsdatumlieferdatum, delivery date, service date
leistung_vonleistungszeitraum von, zeitraum von, period start
leistung_bisleistungszeitraum bis, zeitraum bis, period end
waehrungwährung, currency
steuerwaehrungsteuerwährung, tax currency
kurswechselkurs, exchange rate
profilzugferd profil, profile, format
bezug_rechnungbezug, ursprungsrechnung, stornierte rechnung, preceding invoice, reference invoice
leitweg_idleitweg-id, leitweg, buyer reference, käuferreferenz, kaeuferreferenz
bestellnummerbestell-nr., bestellung, auftrag kunde, order ref, order number, po number
auftragsnummerauftrags-nr., auftrag, seller order ref, sales order
lieferscheinlieferscheinnummer, lieferschein-nr., delivery note
buchungskontokontierung, kostenstelle, accounting ref, accounting reference
referenzihre referenz, vertragsnummer, reference, contract
projektprojektnummer, projekt-nr., project, project ref
projektnameprojektbezeichnung, project name
kopftexteinleitung, anschreiben, header text, intro
fusstextfußtext, schlusstext, bemerkung, anmerkung, notiz, footer
steuerregelungsteuerfall, umsatzsteuerregelung, tax scheme, vat scheme
befreiungsgrundsteuerbefreiungsgrund, exemption reason, tax exemption reason
nachlassgesamtnachlass, rechnungsrabatt, allowance, document discount
nachlass_prozentnachlass %, rechnungsrabatt %, allowance percent, document discount percent
nachlass_grundnachlassgrund, rabattgrund, allowance reason, document discount reason
nachlass2zweiter nachlass, allowance2, allowance 2, document discount 2
nachlass2_prozentnachlass2 %, zweiter nachlass %, allowance2 percent, document discount 2 percent
nachlass2_grundzweiter nachlass grund, allowance2 reason, document discount 2 reason
zuschlagversandkosten, versand, verpackung, nebenkosten, extra charge, shipping
zuschlag_grundzuschlagsgrund, zuschlag bezeichnung, extra charge reason
zuschlag_ustzuschlag ust, zuschlag mwst, extra charge vat
skonto_tageskonto tage, skontofrist, skonto days, discount days
skonto_prozentskonto prozent, skonto %, skonto, skonto percent
skonto2_tageskonto 2 tage, skonto2 days
skonto2_prozentskonto 2 prozent, skonto 2, skonto2 percent
anzahlungbereits gezahlt, vorauszahlung, prepaid, paid amount
rundungrundungsausgleich, rounding
anrechnungabzug, angerechnete rechnungen, anzahlungsrechnungen, vorbelege
weitere_bezuegeweitere bezüge, weitere bezuege, further references
bezug_datumbezugsdatum, datum der ursprungsrechnung, reference date
leistung_voraussichtlichleistung voraussichtlich, voraussichtliche leistung, voraussichtlicher leistungszeitpunkt, expected delivery
leistung_voraussichtlich_bisleistung voraussichtlich bis, voraussichtliche leistung bis, expected delivery to
anzahlung_einganganzahlung eingegangen, anzahlung eingegangen am, anzahlung erhalten am, advance received
auftragssummeauftragssumme netto, auftragswert, order total
absenderabsenderfirma, rechnungssteller, aussteller, eigene firma, sender, issuer
spracherechnungssprache, belegsprache, language, invoice language
zahlungsbedingungenzahlungsbedingung, zahlungskonditionen, konditionen, terms of payment, payment terms text, payment conditions
lieferbedingungenlieferbedingung, lieferkonditionen, incoterms, incoterm, delivery terms, terms of delivery
unser_zeichenunser zeichen, sachbearbeiter, bearbeiter, our reference, our ref, clerk
iban
bicswift
bankbankname, kreditinstitut, bank name
zahlartzahlungsart, zahlungsweise, payment method, payment means
mandatmandatsreferenz, sepa-mandat, mandate, mandate reference
glaeubiger_idgläubiger-id, glaeubiger-id, creditor id, creditor identifier
verwendungszweckzahlungsreferenz, payment reference, remittance information
zahlungsempfaengerzahlungsempfänger, abweichender zahlungsempfaenger, payee, payee name
kundennummerkunden-nr., kundennr, kdnr, partnernummer, debitor, debitorennummer
kunde Requiredkunde_name, kundenname, name, firma, empfaenger, empfänger
kunde_handelsnamehandelsname, kunde handelsname, trading name
kunde_rechtsformrechtsform, gesellschaftsform, legal form
kunde_ansprechpartneransprechpartner, z. hd., zu haenden, kontakt, contact, contact name
kunde_strassekunde_straße, strasse, straße, kunde strasse, anschrift, adresse
kunde_adresszusatzadresszusatz, kunde zusatz, street2, address line 2
kunde_plzplz, postleitzahl, kunde plz, zip, postal code, postcode
kunde_ortort, stadt, kunde ort, city, town
kunde_bundeslandbundesland, region, state, province
kunde_landland, kunde land, country, country code
kunde_ustidustid, ust-idnr, ust-idnr., ust-id, umsatzsteuer-id, kunde ustid
kunde_steuernummersteuernummer, steuer-nr., kunde steuernummer, tax number, tax id
kunde_emailemail, e-mail, mail, kunde email, customer email
kunde_telefontelefon, tel, tel., kunde telefon, phone, telephone
kunde_ibankunden iban, iban kunde, iban des kunden, kunden-iban, empfaenger iban, erstattungskonto
liefer_namelieferanschrift name, lieferadresse name, lieferung an, ship to, ship to name, delivery name
liefer_kennunglieferstelle, standortkennung, ship to id, location id
liefer_strasseliefer_straße, lieferanschrift strasse, lieferstrasse, ship to street, delivery street
liefer_adresszusatzliefer zusatz, ship to street2
liefer_zusatz2liefer zusatz 2, ship to street3
liefer_plzlieferplz, lieferanschrift plz, ship to zip, delivery zip
liefer_ortlieferort, lieferanschrift ort, ship to city, delivery city
liefer_bundeslandliefer region, ship to state
liefer_landlieferland, lieferanschrift land, ship to country, delivery country
position Requiredpos, bezeichnung, leistung, beschreibung, artikel, artikelbezeichnung
artikelnummerartikel-nr., artikelnr, art.-nr., sku, item id, item number
kunden_artikelnummerkundenartikelnummer, artikelnummer kunde, buyer item id, customer item number
gtinean, barcode
mengeanzahl, stück, stueck, stk, qty, quantity
einheitmengeneinheit, me, unit, uom, unit of measure
einzelpreis Requiredpreis, einzelpreis netto, nettopreis, stueckpreis, stückpreis, unit price
netto Requirednettobetrag, positionsnetto, zeilensumme, gesamt netto, net, line net
ust_prozentust, ust %, ust-satz, mwst, mwst %, mwst-satz
rabattpositionsrabatt, rabatt betrag, discount, discount amount
rabatt_prozentrabatt %, rabatt prozent, discount %, discount rate
zuschlag_positionpositionszuschlag, zuschlag position, line surcharge, surcharge
listenpreisbruttolistenpreis, list price, gross price
preisnachlasspreisrabatt, price discount
preisbasispreisbasismenge, preis je menge, price base quantity, base quantity
ursprungslandherkunftsland, origin country, country of origin
positionsnotizpositionstext, zusatztext, line note, item note

No-Code & White-Label

Without code: upload a spreadsheet (Excel, LibreOffice or CSV) under Create from file → preview, then a ZIP with all ZUGFeRD PDFs. Ideal for staff without a technical background.

Make / Zapier: call POST /v1/generate per row with the “Webhooks/HTTP” module, no server of your own needed.

White label embedding: for SaaS providers. Your server mints a short-lived token from your API key and embeds the invoice form in your own corporate design via iframe; the end customer creates no account, credits are charged to your quota:

# 1) Create the token on your server (the API key stays secret)
curl -X POST https://sichere-erechnung.de/v1/embed/token \
  -H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"accent":"#e8590c","title":"Invoice, your brand","ttl":3600}'
# -> { "token": "…", "embed_url": "https://sichere-erechnung.de/embed?token=…" }

# 2) Embed embed_url in an iframe
<iframe src="https://sichere-erechnung.de/embed?token=…" style="width:100%;height:900px;border:0"></iframe>

Theming: accent colour, title and corner radius via the token (accent, title, radius) or cosmetically via URL (?accent=ff8800&radius=4). Your logo comes from your profile.

Recipient contact & catalogue: the embedded form contains an “Attn.” field (BT-56, shown on the invoice) and suggests items from your catalogue via autocomplete (fills unit, net price and VAT), identical to the main interface and under your brand.

Events to the parent window (postMessage): the iframe sends zugferd:resize (height for auto-resize), zugferd:success (PDF created, including filename) and zugferd:error (with errors[]). This is how you connect it:

window.addEventListener('message', function (ev) {
  var d = ev.data || {};
  if (d.type === 'zugferd:resize') iframe.style.height = d.height + 'px';
  if (d.type === 'zugferd:success') console.log('Invoice created:', d.filename);
  if (d.type === 'zugferd:error') console.warn(d.errors);
});

See it live: white label page with embedded demo.

Advance payment, partial and final invoices

Document types: 386 advance payment invoice, 875 construction instalment invoice, 326 partial invoice (completed partial service), 876 construction partial final invoice, 877 construction final invoice. A final invoice is a remainder invoice (German VAT Application Decree, UStAE 14.8(11), and Federal Ministry of Finance (BMF) letter of 15 Oct 2024, paras. 47 and 48): it contains all items of the complete service and, per earlier document and tax rate, one negative line (quantity -1, net price positive). Tax, gross amount and amount due relate only to the remainder. The field invoice.prepaid (BT-113) is not used for this. An invoice 380 with deductions is called “Schlussrechnung” (final invoice) on the document, email, list and dunning letter; in the XML it remains 380.

Final invoice via API

For one of your own advance payment invoices, the number is enough. We add the date, document type, the amount to be deducted and the split per tax rate from your account:

{ "invoice": { "type_code": "380", "number": "SR-2026-001", "issue_date": "2026-09-30",
               "delivery_date": "2026-09-25", "seller_order_ref": "AB-2026-0815",
               "deductions": [ { "number": "AZ-2026-001" } ] },
  "seller": { … }, "buyer": { … },
  "lines": [ { "name": "Conversion of hall 3 as per order AB-2026-0815", "qty": 1, "unit_price": 30000.00, "vat_rate": 19 } ] }

Result for a paid advance payment of 10,710.00 (9,000.00 net): 30,000.00 minus 9,000.00 = 21,000.00 net, 3,990.00 VAT, amount due 24,990.00. An external earlier document (not in your account) needs the net amounts per tax rate; amount alone is only sufficient if the line items have exactly one tax rate (then with a note that net is more precise):

"deductions": [ { "number": "A-77", "issue_date": "2026-03-01", "type_code": "386",
                  "received_date": "2026-03-10", "rates": [ { "vat_rate": 19, "net": 9000.00 } ] } ]

An advance payment invoice states the expected date of the service instead of a date of supply:

{ "invoice": { "type_code": "386", "number": "AZ-2026-001", "issue_date": "2026-07-01",
               "expected_delivery_from": "2026-10", "seller_order_ref": "AB-2026-0815", "order_total_net": 30000.00 },
  "seller": { … }, "buyer": { … },
  "lines": [ { "name": "Advance payment 30 % on order AB-2026-0815", "qty": 1, "unit_price": 9000.00, "vat_rate": 19 } ] }

New fields

FieldMeaning
invoice.deductions[]Deductions, only for 380, 326, 876 and 877. Per entry number (required), optional issue_date, type_code (386, 875, 326 or 876, default 386), received_date, amount (gross amount deducted), rates[] with vat_rate, net and optional tax (short form {number, vat_rate, net}), cancelled_confirmed. A number as text is treated like {"number": …}. For one of your own earlier documents, amount or rates[] count as the confirmed amount and are kept: at most the gross amount of the earlier document, at least the amount received (386, 875) or the partial consideration invoiced (326, 876), with a note if above the amount received; without either, we calculate the suggestion. The same applies to an imported remainder invoice (XML import, /v1/generate with XML). The same advance payment additionally under invoice.prepaid is an error, as it would be deducted twice.
invoice.reference_invoiceReference (BT-25) as text, as a list or as an object {number, issue_date}; an integer is treated as text. With a list, the first number becomes the reference and the others become references[]. A decimal number or a boolean returns 422.
invoice.references[]further references {number, issue_date?, type_code?}. EXTENDED writes all of them as BG-3; EN 16931, XRechnung, BASIC and BASIC WL write one and the rest as the sentence “Weitere Bezüge: …” (further references) in BT-22; MINIMUM writes none.
invoice.reference_issue_dateDate of the first reference (BT-26), YYYY-MM-DD.
invoice.expected_delivery_fromfor 386, 875 and their cancellations: expected date of the service, YYYY-MM-DD or YYYY-MM (calendar month, § 31(4) of the German VAT Implementing Ordinance, UStDV). With invoice.expected_delivery_to a period; only _to means “by”, both empty means “not yet agreed”. For other document types a note is given and the field is ignored.
invoice.advance_received_datefor 386 and 875: the day the advance payment was received, not after the invoice date. If it differs from the invoice date, it appears as BT-7 in the XML (EN 16931, XRechnung, EXTENDED); BASIC and BASIC WL mention it in the note.
invoice.order_total_netNet order total, not in the XML; used for the percentage helper and “invoiced so far” in the form.
invoice.prepaidBT-113 now means “already paid, without an advance payment invoice”. The sign follows the gross amount (negative only for a negative gross amount, e.g. in a cancellation); above the gross amount it is an error instead of being silently capped; only numbers and numeric text with a decimal point. For MINIMUM the amount due is in DuePayableAmount. Deduct advance payment invoices with invoice.deductions: If an advance payment invoice was issued for this advance payment, the deduction of the VAT it contains is missing this way (§ 14(5) sentence 2 UStG). Under UStAE 14.8(10) the entire tax shown may then be owed; please clarify this with your tax adviser. Deduct advance payment invoices with invoice.deductions.

What is deducted

Notes on the document (BT-22 and PDF)

Cancellation and correction

A 384 referencing one of your own documents with exactly its negated gross amount is a full cancellation and closes it; a smaller negative amount is a partial correction, a positive one a supplementary charge. Cancelling a final invoice reverses its deductions, and the advance payment invoices become open again. Cancelling or correcting a deducted advance payment invoice is blocked as long as the final invoice is effective, even if the reference comes as a list. A cancellation takes over invoice.prepaid negated (line “plus reversal of amount already paid” in the PDF) and carries no deductions: Cancellation with an amount already paid (invoice.prepaid, BT-113): this document only refunds the amount due. Refund the amount already paid separately or carry it over to the new invoice.

Notes and headers

Receivables and payments

GET /v1/receivables additionally returns per entry type_code, prepaid_cents, due_cents (amount due), claim_cents (claim after offsetting, null = amount due), open_cents, overpaid_cents, closed_reason (payment, offset offset, superseded superseded, cancelled, covered_by_prepayment, nothing_due) and links.predecessors[] / links.successors[] with {number, type_code, kind, applied_cents} (kind: offset, cancellation, correction, reference). payments[] carry value_date, kind (payment, settlement, refund) and note. Credit notes, cancellation and correction documents are not in the list. paid_cents is the sum of real payments, without capping.

Incoming invoice check

POST /v1/check reads BT-113, BT-114 and BT-115 and additionally returns prepaid_total and due_payable under invoice (decimal number like grand_total). The finding zahlbetrag (amount due) reports a deducted advance payment (note), a contradiction of more than 1 cent between the amount due and gross minus advance payment plus rounding, a negative BT-113 with a positive gross amount or rounding above 0.99 (yellow, can only be marked for payment with confirmation) and, for MINIMUM, a smaller amount due (note). If the amount due differs from the gross amount and is not in the readable PDF, consistency reports yellow. The SEPA bulk payment transfers the amount due, not the gross amount; an amount due of 0 cannot be marked for payment, nor can an invoice in a foreign currency (an unreadable currency appears as XXX). Documents from before this change without a read amount due are confirmed once in the payment run box.

Import and AI

The import column belegart (document type) accepts “Schlussrechnung”, “Endrechnung”, “Restrechnung” (final/remainder invoice, 380), “Teilschlussrechnung” (876), “Schlussrechnung Bau” (877), “Abschlagsrechnung” (386, with “Bau” 875). New columns: anrechnung (numbers, separated by “;” or “,”), leistung_voraussichtlich and _bis, anzahlung_eingang, weitere_bezuege, bezug_datum, auftragssumme. A deduction only works on invoices that have already been created: import the advance payment invoice and the final invoice in two runs. Several amounts in anzahlung are a row error. The template shows both (advance payment invoice 2026-002, final invoice 2026-003). /v1/import lists row errors and notes per invoice under invoices[i].errors and invoices[i].hints, and additionally fehlerJeRechnung and hinweiseJeRechnung in info; an invoice with a row error is not created. The AI capture (/v1/automap) recognises advance payment, partial and final invoices and “abzüglich Anzahlung AR-…” (less advance payment) as a deduction.

Word template

New placeholders: ${service_label}, ${service_text}, ${prepaid_line}, ${net_before_deductions}, ${deductions_net}, ${advance_note}, ${grand_label}, plus the blocks ${deductions_block}, ${net_block} and ${prepaid_block}. Templates created before this change do not know ${service_text}, ${due_total} or ${advance_note}: advance payment invoices, documents with an amount already paid and documents with a note are then created in the standard layout, the template surcharge is refunded and the reason is given in X-Template-Warning. Remedy: “Regenerate template” in your profile.

Intended changes for existing calls

Example with notes in the header: curl -i -X POST https://sichere-erechnung.de/v1/generate … shows X-Invoice-Hints in the response.

Machine-readable endpoint overview: https://sichere-erechnung.de/v1 (JSON)

Log in

Forgot your password?

No account yet? Sign up now

Sign up

B2B offer for businesses and self-employed professionals; a tax number or VAT ID is required.

At least one of the two is required.

Already registered? Log in