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
| Standard | ZUGFeRD 2.x / Factur-X 1.0, selectable via invoice.profile (default: en16931/Comfort) |
| Syntax | UN/CEFACT CII (Cross Industry Invoice, D16B) |
| Container | PDF/A-3 with embedded factur-x.xml (AFRelationship: Alternative); only with profile xrechnung pure XML instead of PDF |
| Validated with | official KoSIT validator, accepts EN 16931 & XRechnung (CII + UBL) |
| Document types | Invoice (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.profile | When to use |
|---|---|
en16931 (default) | Comfort: suits practically every B2B invoice and is accepted by almost any software |
xrechnung | Mandatory 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 |
basic | leaner variant of Comfort with line items, but without the optional additional fields |
basicwl | like basic, but without line items in the XML: for flat-rate or subscription invoices |
minimum | pure booking message (no line items, no contact details); many systems and authorities do not accept this as a full invoice |
extended | largest feature set: including lines[].surcharge (line surcharge) and invoice.extra_charge_* (document-level charge, e.g. shipping) |
Quick start
https://sichere-erechnung.decurl -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
- A company other than the key's own requires group permission (tick it when creating the key in the dashboard), otherwise
403. Every key may name its own company. - Unknown, ambiguous, deactivated or a company of another group:
422; the request never silently runs in a different company. Header and field at the same time but pointing to different companies: also422. - Billing uses the balance of the company group; the rate limit applies per key, an
Idempotency-Keyper addressed company. The key of a deactivated company responds with403. GET /v1/companiestells you which companies a key may address (free of charge, also with a read-only key): per companyid,code,name,countryandvat_id; without group permission only its own.- The VAT ID or tax number in
sellermust match the addressed company. If it belongs to another company in the group, the API responds with422and names that company, instead of silently creating the invoice in the wrong company.
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
| Method | Path | Function |
|---|---|---|
POST | /v1/cii | JSON invoice → EN 16931 CII XML (for your own PDF layouts) |
POST | /v1/generate | JSON 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/embed | Your 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/import | Import 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}/zip | Result ZIP of a finished run, kept for 7 days. Free of charge |
POST | /v1/automap | AI 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/validate | XML 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/check | XML 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/seal | Invoice (JSON as for /generate OR core fields) → authenticity seal (Ed25519), returns verify_url |
POST | /v1/extract | ZUGFeRD PDF → embedded XML + core fields |
POST | /v1/validate/fraud | XML 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/diff | Compare two invoices (multipart original + corrected) → line and header diff as JSON, with ?format=html as an embeddable Git-style diff (red/green) |
GET | /v1/receivables | Open 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/payment | Record 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/paid | Record the open balance as settled: {"number": "R-1", "value_date": "2026-09-24"}. Nothing open: 409. Free of charge |
POST | /v1/receivables/refund | Record 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/partners | Business 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/partners | Create 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/archive | Archive 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/companies | Companies 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/token | White 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 | /v1 | Machine-readable endpoint overview including current credit costs (no key needed) |
POST | /v1/dunning | Dunning 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 | /health | Readiness (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_number | Tax number as an alternative to seller.vat_id (VAT ID) |
buyer.vat_id | Buyer's VAT ID (BT-48): required with invoice.tax_scheme: reverse_charge or innergemeinschaftlich (see below) |
invoice.due_date | Due date (BT-9); if omitted, the invoice states “payable immediately without deduction” |
invoice.delivery_date | Date 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.bic | Customer'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_reference | Leitweg ID (BT-10, routing ID for German public authorities): required with profile xrechnung, otherwise optional |
lines[].net | explicit net line amount instead of unit_price×qty (overrides the calculation, e.g. for odd flat rates) |
lines[].surcharge | Line surcharge (BG-28, absolute amount), counterpart to discount; only useful with profile extended |
invoice.extra_charge_amount / _reason / _vat_rate | document-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.rechtsform | Legal form (e.g. “GmbH”), appended to the company name instead of replacing it |
seller.register_court / register_number | Registry 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_directors | Managing directors / board as free text (BT-33, together with the registry court) |
seller.trading_name | Trading name, if different from the company name (BT-28) |
seller.website / seller.logo | optional 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_text | Introductory text (BT-22) |
invoice.footer | Footer (e.g. managing director, commercial register number, tax notes) |
invoice.reference | Contract or customer reference (BT-12) |
invoice.payment_terms | Payment 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_hide | true: 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_terms | Delivery 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_account | Optional: 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.reference | Payment 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_reason | Allowance 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_reason | Second 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_rate | Foreign 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_date | Origin 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_name | Project number (BT-11) and optional project name; only in the XML with en16931/xrechnung/extended |
invoice.seller_order_ref | Your sales order / order confirmation number (BT-14); counterpart to the buyer's purchase order number order_ref; en16931 and above only |
seller.street2 / buyer.street2 | Additional address line (BT-36/BT-51, e.g. building, PO box) |
seller.state / buyer.state | State/region (BT-39/BT-54) |
lines[].item_id / lines[].buyer_item_id | Seller's item number (BT-155) or buyer's item number (BT-156); en16931 and above only |
lines[].gtin | GTIN/EAN (BT-157, 8/12/13/14 digits, check digit is validated), in the XML as GlobalID schemeID="0160" |
lines[].list_price / lines[].price_discount | List 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_qty | Price base quantity (BT-149), e.g. 100 for “price per 100 pieces”; line amount = quantity × price ÷ base quantity |
lines[].origin_country | Country of origin of the goods (BT-159, ISO code); en16931 and above only |
lines[].note | Line note (BT-127), also shown on the PDF |
lines[].unit | Unit 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_percent | Discount per line item (absolute amount or percent; EN 16931 line allowance) |
invoice.tax_scheme | standard · 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_end | Service 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_ref | Buyer's purchase order number (BT-13) · delivery note (BT-16) · account assignment / cost centre (BT-19) |
invoice.prepaid / rounding | amount 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_bank | Optional: 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.method | transfer (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.name | different payee (BG-10, e.g. assignment/factoring) |
buyer.contact_name · buyer.email · buyer.phone | Buyer contact (BG-9): name (BT-56, shown as “Attn.” on the invoice), email (BT-58), phone (BT-57) |
buyer.partner_no | Your 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.phone | Seller contact (BG-6): name (BT-41), email (BT-43), phone (BT-42) |
invoice.type_code | 380 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.language | Document 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_invoice | Reference 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.
| Code | Meaning |
|---|---|
C62 | Units |
H87 | Piece |
EA | Each |
PCE | Piece (PCE) |
NAR | Number of articles |
PR | Pair |
SET | Set |
LS | Lump sum |
P1 | Percent |
E48 | Service unit |
E49 | Working day |
NPR | Number of pairs |
MIN | Minutes |
HUR | Hours |
DAY | Days |
WEE | Weeks |
MON | Months |
ANN | Years |
SEC | Seconds |
QAN | Quarters |
MGM | Milligrams |
GRM | Grams |
KGM | Kilograms |
TNE | Tonnes |
MLT | Millilitres |
LTR | Litres |
HLT | Hectolitres |
MTQ | Cubic metres |
CMQ | Cubic centimetres |
MMT | Millimetres |
CMT | Centimetres |
MTR | Metres |
KMT | Kilometres |
MTK | Square metres |
CMK | Square centimetres |
MMK | Square millimetres |
HAR | Hectares |
KWH | Kilowatt hours |
MWH | Megawatt hours |
KWT | Kilowatts |
WHR | Watt hours |
XPK | Package |
XPP | Piece, unpacked |
XBX | Box |
XCT | Carton |
XPA | Packet |
XBG | Bag |
XRO | Roll |
XBO | Bottle |
XCA | Can |
XTU | Tube |
XPX | Pallet |
XCS | Crate |
XCR | Case |
XSA | Sack |
XCY | Cylinder |
XBJ | Bucket |
KMH | Kilometres per hour |
D64 | Calendar days |
DZN | Dozen |
KT | Kit |
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.
- When creating:
/v1/generatewith"seal": true→ the QR code is embedded directly in the ZUGFeRD PDF, theverify_urlcomes in the headerX-Seal-Verify-Url(+2 credits, customer key, IBAN required). - Your own PDF:
/v1/embedwith"seal": true→ the seal is created,verify_urlin the header; your layout remains unchanged (place the QR code yourself). - Seal only:
/v1/seal(JSON as for /generate or core fields) → returnsverify_urland token, without a PDF. With core fields, optionallytype_code(document type, e.g.381for a credit note): the verification page then names the document type and, for a credit note, shows the IBAN as the customer's refund account; without it, the page refers neutrally to a document. - Several bank accounts: if the invoice carries an additional bank account (
invoice.extra_bank), the seal confirms all IBANs; the verification page lists them under “Confirmed bank accounts” with name and bank country./v1/sealadditionally returnsfields.extra_ibans([{"iban", "label"}], empty without); with core fields,extra_ibansis optional as a list of IBANs or{"iban", "label"}. The existing fields and all older seals remain valid unchanged. - Verify:
GET /verify/{token}, public, no key, no credit. Status:valid,revoked,tampered,notfound. - Issuer, VAT ID and IBAN are those of your company:
/v1/sealtakes them from your profile and bank accounts. Missing details are filled in, deviating ones are rejected with422(for a credit note or direct debit the IBAN is the customer's account and is not checked). The currency is a three-letter code, e.g.EUR. The embedded white-label form always takes bank account and sender from the profile of the token holder, never from the request. - White label: The verification page shows the issuer's logo and name (from their profile).
- Revocation: After a cancellation or correction, revoke the seal in the dashboard; the verification page then shows “revoked”.
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:
$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 XMLimport 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
- Credits per call:
/v1/cii,/v1/generate,/v1/embed,/v1/validate,/v1/extract,/v1/diff,/v1/dunning= 1;/v1/check(incoming invoice check) = 2;/v1/seal(authenticity seal) = 2;/v1/validate/fraud= 3;/v1/automap(AI import) = 10. - Surcharges for
/v1/generate:"template": true(your own Word template) +2, per embedded attachment +1,"seal": true+2. - Surcharge for
/v1/automap:"catalog": true(catalogue already used in the AI recognition instead of only in post-processing) → 30 instead of 10 credits (three times, more tokens per call). - Free of charge (but key + rate limit):
/v1/receivables,/v1/receivables/payment,/v1/receivables/paid,/v1/receivables/refund,/v1/partners,/v1/partners/archive,/v1/companies,/v1/embed/token,GET /v1. Free without a key:/verify/{token}and/health. - Balance empty →
402 Payment Required(top up credits in the shop). - Rate limit: 60 requests/minute per key; if exceeded,
429withRetry-After. Every response carriesX-RateLimit-Remaining. - You only pay for a delivered result: If a call is rejected, whether due to a server error (
5xx) or because the data is not EN 16931 compliant (422), we refund the credits used automatically, including any surcharges. Rejections due to insufficient balance, rate limit or a duplicate invoice number cost nothing anyway. Conversely, a validation result of “not compliant” is a result and is charged, since that is what you requested the check for (it comes as200withvalid: false).
Error codes
| Code | Meaning |
|---|---|
401 | Missing or invalid API key |
402 | No balance, top up credits |
403 | Email not yet confirmed, read-only key used for a write call, X-Company without group permission, or company deactivated |
422 | Input 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 |
429 | Rate 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.
| Column | also recognised as |
|---|---|
rechnungsnummer Required | rechnungs-nr., rechnungsnr, rechnung nr, re-nr, nummer, belegnummer |
belegart | dokumentart, typ, type code, invoice type, document type |
datum | rechnungsdatum, belegdatum, date, invoice date, issue date |
faellig | fällig, faelligkeit, faelligkeitsdatum, zahlbar bis, due, due date |
zahlungsziel_tage | zahlungsziel, zahlungsfrist, zahlungsziel tage, payment term days, payment terms, net days |
leistungsdatum | lieferdatum, delivery date, service date |
leistung_von | leistungszeitraum von, zeitraum von, period start |
leistung_bis | leistungszeitraum bis, zeitraum bis, period end |
waehrung | währung, currency |
steuerwaehrung | steuerwährung, tax currency |
kurs | wechselkurs, exchange rate |
profil | zugferd profil, profile, format |
bezug_rechnung | bezug, ursprungsrechnung, stornierte rechnung, preceding invoice, reference invoice |
leitweg_id | leitweg-id, leitweg, buyer reference, käuferreferenz, kaeuferreferenz |
bestellnummer | bestell-nr., bestellung, auftrag kunde, order ref, order number, po number |
auftragsnummer | auftrags-nr., auftrag, seller order ref, sales order |
lieferschein | lieferscheinnummer, lieferschein-nr., delivery note |
buchungskonto | kontierung, kostenstelle, accounting ref, accounting reference |
referenz | ihre referenz, vertragsnummer, reference, contract |
projekt | projektnummer, projekt-nr., project, project ref |
projektname | projektbezeichnung, project name |
kopftext | einleitung, anschreiben, header text, intro |
fusstext | fußtext, schlusstext, bemerkung, anmerkung, notiz, footer |
steuerregelung | steuerfall, umsatzsteuerregelung, tax scheme, vat scheme |
befreiungsgrund | steuerbefreiungsgrund, exemption reason, tax exemption reason |
nachlass | gesamtnachlass, rechnungsrabatt, allowance, document discount |
nachlass_prozent | nachlass %, rechnungsrabatt %, allowance percent, document discount percent |
nachlass_grund | nachlassgrund, rabattgrund, allowance reason, document discount reason |
nachlass2 | zweiter nachlass, allowance2, allowance 2, document discount 2 |
nachlass2_prozent | nachlass2 %, zweiter nachlass %, allowance2 percent, document discount 2 percent |
nachlass2_grund | zweiter nachlass grund, allowance2 reason, document discount 2 reason |
zuschlag | versandkosten, versand, verpackung, nebenkosten, extra charge, shipping |
zuschlag_grund | zuschlagsgrund, zuschlag bezeichnung, extra charge reason |
zuschlag_ust | zuschlag ust, zuschlag mwst, extra charge vat |
skonto_tage | skonto tage, skontofrist, skonto days, discount days |
skonto_prozent | skonto prozent, skonto %, skonto, skonto percent |
skonto2_tage | skonto 2 tage, skonto2 days |
skonto2_prozent | skonto 2 prozent, skonto 2, skonto2 percent |
anzahlung | bereits gezahlt, vorauszahlung, prepaid, paid amount |
rundung | rundungsausgleich, rounding |
anrechnung | abzug, angerechnete rechnungen, anzahlungsrechnungen, vorbelege |
weitere_bezuege | weitere bezüge, weitere bezuege, further references |
bezug_datum | bezugsdatum, datum der ursprungsrechnung, reference date |
leistung_voraussichtlich | leistung voraussichtlich, voraussichtliche leistung, voraussichtlicher leistungszeitpunkt, expected delivery |
leistung_voraussichtlich_bis | leistung voraussichtlich bis, voraussichtliche leistung bis, expected delivery to |
anzahlung_eingang | anzahlung eingegangen, anzahlung eingegangen am, anzahlung erhalten am, advance received |
auftragssumme | auftragssumme netto, auftragswert, order total |
absender | absenderfirma, rechnungssteller, aussteller, eigene firma, sender, issuer |
sprache | rechnungssprache, belegsprache, language, invoice language |
zahlungsbedingungen | zahlungsbedingung, zahlungskonditionen, konditionen, terms of payment, payment terms text, payment conditions |
lieferbedingungen | lieferbedingung, lieferkonditionen, incoterms, incoterm, delivery terms, terms of delivery |
unser_zeichen | unser zeichen, sachbearbeiter, bearbeiter, our reference, our ref, clerk |
iban | |
bic | swift |
bank | bankname, kreditinstitut, bank name |
zahlart | zahlungsart, zahlungsweise, payment method, payment means |
mandat | mandatsreferenz, sepa-mandat, mandate, mandate reference |
glaeubiger_id | gläubiger-id, glaeubiger-id, creditor id, creditor identifier |
verwendungszweck | zahlungsreferenz, payment reference, remittance information |
zahlungsempfaenger | zahlungsempfänger, abweichender zahlungsempfaenger, payee, payee name |
kundennummer | kunden-nr., kundennr, kdnr, partnernummer, debitor, debitorennummer |
kunde Required | kunde_name, kundenname, name, firma, empfaenger, empfänger |
kunde_handelsname | handelsname, kunde handelsname, trading name |
kunde_rechtsform | rechtsform, gesellschaftsform, legal form |
kunde_ansprechpartner | ansprechpartner, z. hd., zu haenden, kontakt, contact, contact name |
kunde_strasse | kunde_straße, strasse, straße, kunde strasse, anschrift, adresse |
kunde_adresszusatz | adresszusatz, kunde zusatz, street2, address line 2 |
kunde_plz | plz, postleitzahl, kunde plz, zip, postal code, postcode |
kunde_ort | ort, stadt, kunde ort, city, town |
kunde_bundesland | bundesland, region, state, province |
kunde_land | land, kunde land, country, country code |
kunde_ustid | ustid, ust-idnr, ust-idnr., ust-id, umsatzsteuer-id, kunde ustid |
kunde_steuernummer | steuernummer, steuer-nr., kunde steuernummer, tax number, tax id |
kunde_email | email, e-mail, mail, kunde email, customer email |
kunde_telefon | telefon, tel, tel., kunde telefon, phone, telephone |
kunde_iban | kunden iban, iban kunde, iban des kunden, kunden-iban, empfaenger iban, erstattungskonto |
liefer_name | lieferanschrift name, lieferadresse name, lieferung an, ship to, ship to name, delivery name |
liefer_kennung | lieferstelle, standortkennung, ship to id, location id |
liefer_strasse | liefer_straße, lieferanschrift strasse, lieferstrasse, ship to street, delivery street |
liefer_adresszusatz | liefer zusatz, ship to street2 |
liefer_zusatz2 | liefer zusatz 2, ship to street3 |
liefer_plz | lieferplz, lieferanschrift plz, ship to zip, delivery zip |
liefer_ort | lieferort, lieferanschrift ort, ship to city, delivery city |
liefer_bundesland | liefer region, ship to state |
liefer_land | lieferland, lieferanschrift land, ship to country, delivery country |
position Required | pos, bezeichnung, leistung, beschreibung, artikel, artikelbezeichnung |
artikelnummer | artikel-nr., artikelnr, art.-nr., sku, item id, item number |
kunden_artikelnummer | kundenartikelnummer, artikelnummer kunde, buyer item id, customer item number |
gtin | ean, barcode |
menge | anzahl, stück, stueck, stk, qty, quantity |
einheit | mengeneinheit, me, unit, uom, unit of measure |
einzelpreis Required | preis, einzelpreis netto, nettopreis, stueckpreis, stückpreis, unit price |
netto Required | nettobetrag, positionsnetto, zeilensumme, gesamt netto, net, line net |
ust_prozent | ust, ust %, ust-satz, mwst, mwst %, mwst-satz |
rabatt | positionsrabatt, rabatt betrag, discount, discount amount |
rabatt_prozent | rabatt %, rabatt prozent, discount %, discount rate |
zuschlag_position | positionszuschlag, zuschlag position, line surcharge, surcharge |
listenpreis | bruttolistenpreis, list price, gross price |
preisnachlass | preisrabatt, price discount |
preisbasis | preisbasismenge, preis je menge, price base quantity, base quantity |
ursprungsland | herkunftsland, origin country, country of origin |
positionsnotiz | positionstext, 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
| Field | Meaning |
|---|---|
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_invoice | Reference (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_date | Date of the first reference (BT-26), YYYY-MM-DD. |
invoice.expected_delivery_from | for 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_date | for 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_net | Net order total, not in the XML; used for the percentage helper and “invoiced so far” in the form. |
invoice.prepaid | BT-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
- 386 and 875: the amount received, pro rata per tax rate. If the advance payment invoice is unpaid or only partly paid, the final invoice claims the remainder; the advance payment invoice counts as superseded and is no longer dunned.
- 326 and 876: the partial consideration invoiced, including tax, regardless of payment; the partial invoice remains a separate claim.
- Explicit
amountfor one of your own earlier documents: at most its amount, at least the amount received (386, 875) or the partial consideration invoiced (326, 876), otherwise422(§ 14(5) sentence 2 of the German VAT Act, UStG). More than received counts as confirmation: the difference remains open on the advance payment invoice and continues to be dunned; the note states both amounts. - Same currency and tax scheme; an earlier document can only be in one effective final invoice. A cancelled, paid earlier document only with
cancelled_confirmed: trueand without a recorded refund (UStAE 14.8(9)).
Notes on the document (BT-22 and PDF)
- Remainder invoice, if a negative line is created: “Remainder invoice: the deducted partial considerations are subtracted net as negative lines. Only the VAT on the remaining consideration is shown.”
- For each of your own advance payment invoices that is not fully deducted: “The advance payment invoice AZ-2 of 1 Aug 2026 has not been paid. Its amount is included in this invoice and is not payable separately.” or “Of the advance payment invoice AZ-2 of 1 Aug 2026, EUR 4,000.00 has been received and deducted. The remainder is included in this invoice and is not payable separately.” If more is confirmed than received: “… EUR 4,000.00 has been received. EUR 8,000.00 has been deducted. The difference of EUR 4,000.00 remains payable on the advance payment invoice AZ-2. …” (The notes are printed on the invoice in German.)
- Advance payment invoice: “Advance payment invoice: this invoice bills an advance payment before the service is performed.”, construction instalment invoice: “Instalment invoice: this invoice bills an instalment payment before acceptance of the construction work.”, plus the expected date. No date of supply (BT-72) and no service period (BG-14); a
delivery_dateorperiod_*sent along becomes the expected date, with a note. Advance payment invoices created before this change stay as issued when downloaded again. - XRechnung: a 386 is written as 380 with the note “Anzahlungsrechnung:” (advance payment invoice; FAQ 4.5 of the German Federal Chamber of Tax Advisers, BStBK; BR-DE-17 is only a warning). 875, 876 and 877 remain. A 384 requires
invoice.reference_invoicethere (§ 31(5) sentence 2 UStDV, BR-DE-26); the other profiles give a note.
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
X-Invoice-Hintson PDF and XML responses (/v1/generate,/v1/automap?generate=1,/v1/embed,/v1/ciias XML): notes from the check, separated by “ | ”, at most 2000 bytes, then “und N weitere Hinweise” (and N more notes). JSON responses carryhints(only if there are any).- With
?validate=1the warnings and information from the KoSIT validator go into the same channel. Valid means the validator recommends acceptance;/v1/validate,/v1/validate/fraudand/v1/check(underkosit.hints) return the notes separately from the errors. If the check ends without a check report (timeout), the document counts as not checked: “Check not completed: the KoSIT validator did not return a check report (timeout or abort). Please check again.” X-Invoice-Stored: 0: the PDF has been delivered, but saving it to your invoice list failed (recorded in the error log).X-Invoice-PayLink: 0: the payment link is missing, the invoice is saved.
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.
POST /v1/receivables/payment{number, amount_cents, value_date?, note?}: a date in the future or anamount_centsthat is not a whole number returns422; an overpayment is recorded and stated inoverpaid_cents; cancellation and correction documents do not accept payments (409).POST /v1/receivables/paid{number, value_date?}: records the open balance as settled; nothing open returns409.POST /v1/receivables/refund{number, amount_cents, value_date?, note?}: refund of an overpayment, up tooverpaid_centsat most, otherwise409.
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
invoice.prepaidabove the gross amount is an error instead of being silently capped; negative only for a negative gross amount.- 386 and 875 no longer carry a date of supply; XRechnung with 386 is no longer an error but 380 with a note.
- A negative unit price without a list price is flipped (quantity and price multiplied by -1, same amount), with a note.
lines[].net: nullor""now calculates like a missing field (quantity times price), previously as a net amount of 0.- The receivables API records overpayments instead of capping them and no longer lists cancellation and correction documents.
/v1/ciiwithAccept: application/jsonand/v1/import: for a negative invoice amount (cancellation),totals.prepaidis now 0 andtotals.duethe negative amount; previously the gross amount was shown as already paid and the amount due as 0.- A second full cancellation of the same invoice returns
422(“R-100 ist bereits durch ST-102 storniert.”, i.e. R-100 has already been cancelled by ST-102) instead of200. - Leading and trailing spaces in
invoice.reference_invoiceare removed (BT-25). Documents already saved are reproduced as issued when downloaded again. - The cancellation of one of your own advance payment or construction instalment invoices (386, 875) carries no date of supply (BT-72), and a note is added in
X-Invoice-Hints. invoice.prepaidas text with a decimal comma ("100,50") returns422; previously it silently became 100.00.- Profile
minimumwithinvoice.prepaidorinvoice.rounding:DuePayableAmountis the amount due instead of the gross amount. Documents already saved are reproduced as issued when downloaded again.
Example with notes in the header: curl -i -X POST https://sichere-erechnung.de/v1/generate … shows X-Invoice-Hints in the response.