Invoices

Generate compliant invoice XML

Validates first: a fatal rule failure returns 422 with the same teaching errors as /v1/validate, and does not consume a document. Supported profiles in this build: en16931, xrechnung-ubl, peppol-bis-3, xrechnung-cii, facturx-en16931.

THE PROFILE CHOOSES THE SYNTAX. xrechnung-cii and facturx-en16931 come back as UN/CEFACT CII; every other profile comes back as UBL 2.1. The JSON response says which in its syntax field.

"profile": "auto" — the API chooses. Deterministic rules on the invoice's buyer, no network call, first match wins: a Leitweg-ID (electronic address scheme 0204, or a German buyer reference that is one) → xrechnung-ubl; a Belgian buyer or a Peppol electronic address (any Peppol scheme but 0225) → peppol-bis-3; a French buyer → facturx-en16931; a business in Germany or Austria → xrechnung-cii, or facturx-en16931 with ?format=pdf; anything else → en16931, or facturx-en16931 with ?format=pdf. The answer names the choice: profile_choice in the JSON envelope and the 422, and Attestwire-Profile / Attestwire-Profile-Rule headers on every response, the XML and the PDF included. When the choice is a profile the PDF cannot carry (XRechnung, Peppol) and ?format=pdf was asked for, the answer is 400 unsupported_profile with the reason, free. Naming a profile always overrides auto.

CII output is XML, not a PDF — unless you ask for the PDF. Factur-X and ZUGFeRD files are CII XML inside a PDF/A-3 container. By default this endpoint returns the XML only: no container, no factur-x.xml attachment, no /AFRelationship, so a facturx-en16931 JSON or XML response is the payload and not a Factur-X document.

?format=pdf — the Factur-X / ZUGFeRD PDF (EN 16931 profile). With profile facturx-en16931 (the only profile it accepts), the response is application/pdf: an A4 invoice drawn from your JSON — parties, lines, VAT breakdown, totals, payment details — written as PDF/A-3B, with the CII embedded as factur-x.xml, /AFRelationship /Alternative, and the Factur-X XMP (fx:ConformanceLevel EN 16931). Every amount on the page comes from the same computation as the XML. It is validated first and metered as ONE document, exactly like the XML. On the free plan it is a PREVIEW: the same file with "PREVIEW – not for sending" across every page, and the response carries Attestwire-Preview: watermarked. A key on a paid plan (Solo, Starter, Scale) gets it without the watermark or the header; moving the key to one is how the watermark is removed.

The page, and its options. The body may be the invoice alone, as for every other format, or {"invoice": …, "pdf": {…}} with PdfOptions beside it; the simple options are also query parameters. The page is in English, German or French — the seller's country decides (DE and AT German, FR French, anything else English) unless language says otherwise — with numbers and dates written that language's way, and Content-Language names the one used. The embedded XML is identical in every language. PDF options are read only with ?format=pdf; a malformed wrapper or option is 400 bad_request, charged nothing. Glyphs cover Latin (incl. Latin Extended-A), Greek and Cyrillic; any other character is drawn as "?" and counted in X-Unrendered-Characters — the embedded XML is unaffected. You cannot supply your own PDF to wrap. The generated file passed veraPDF's PDF/A-3B profile in release testing; that is a check of the generator on sample invoices, not of each response.

Output verification is not uniform, and is never per-request. The generator's xrechnung-cii fixture documents are run through the official KoSIT validator on release and accepted. The facturx-en16931 fixtures are not: that profile's BT-24 is the core EN 16931 one, which matches no XRechnung scenario, so KoSIT answers "no scenario matched" instead of a verdict, and an absence of a verdict is not a pass. Neither case says anything about YOUR document: this endpoint does not send anything to KoSIT.

The JSON envelope carries provenance: the engine version and rule set that cleared this document. ?format=xml returns the bytes alone and therefore carries none — an XML document has nowhere to put it, and this API does not write its own metadata into a customer's tax document. Read GET /v1/versions if you need it for a raw-XML pipeline.

post/v1/generate

Request

  • Base URL: https://api.attestwire.com
  • URL: https://api.attestwire.com/v1/generate
  • Auth: HTTP bearer

Query parameters

format'json' | 'xml' | 'pdf'

Set to xml to receive the raw document as application/xml instead of a JSON envelope. Set to pdf for the Factur-X / ZUGFeRD PDF (application/pdf): profile facturx-en16931 only, on every plan (a watermarked preview on the free plan).

language'en' | 'de' | 'fr'

The language of the page: its labels, and the way it formats numbers and dates. Default: the seller country's — DE and AT German, FR French, anything else English. The invoice's own text (names, descriptions, notes) and the embedded XML are the same in every language. Query parameter: language.

?format=pdf only: the language PDF option, as a query parameter. The language of the page: its labels, and the way it formats numbers and dates. Default: the seller country's — DE and AT German, FR French, anything else English. The invoice's own text (names, descriptions, notes) and the embedded XML are the same in every language. Query parameter: language.

payment_qrboolean

The EPC payment QR code ("GiroCode"), printed beside the payment details so a banking app can fill in the transfer: payee, IBAN, amount due and reference. Default: printed whenever the invoice can carry one — currency EUR, payment means 30, 42 or 58 (credit transfer), a valid IBAN, an amount due from 0.01 EUR, and not a credit note — and left out otherwise. false leaves it out; true requires it, and an invoice that cannot carry it is then a 400 that says why. The reference in the code is the payment reference (BT-83), or else the invoice number. Query parameter: payment_qr=true|false.

?format=pdf only: the paymentQr PDF option, as a query parameter. The EPC payment QR code ("GiroCode"), printed beside the payment details so a banking app can fill in the transfer: payee, IBAN, amount due and reference. Default: printed whenever the invoice can carry one — currency EUR, payment means 30, 42 or 58 (credit transfer), a valid IBAN, an amount due from 0.01 EUR, and not a credit note — and left out otherwise. false leaves it out; true requires it, and an invoice that cannot carry it is then a 400 that says why. The reference in the code is the payment reference (BT-83), or else the invoice number. Query parameter: payment_qr=true|false.

payment_linkstring uri

An https:// address where the invoice can be paid online, such as a payment page from your payment provider. It is printed under the payment details and is clickable in the PDF (a link annotation, which PDF/A permits; no script). At most 500 characters, no user name or password. It is on the page only: EN 16931 has no field for it, so the embedded XML does not carry it. Query parameter: payment_link (URL-encoded).

?format=pdf only: the paymentLink PDF option, as a query parameter. An https:// address where the invoice can be paid online, such as a payment page from your payment provider. It is printed under the payment details and is clickable in the PDF (a link annotation, which PDF/A permits; no script). At most 500 characters, no user name or password. It is on the page only: EN 16931 has no field for it, so the embedded XML does not carry it. Query parameter: payment_link (URL-encoded).

Request body

OR

Example request

{
  "currency": "EUR",
  "invoiceTypeCode": "380",
  "seller": {
    "vatId": "DE123456789",
    "address": {
      "countryCode": "DE"
    },
    "electronicAddress": {
      "schemeId": "9930"
    }
  },
  "buyer": {
    "vatId": "DE123456789",
    "address": {
      "countryCode": "DE"
    },
    "electronicAddress": {
      "schemeId": "9930"
    }
  },
  "taxRepresentative": {
    "address": {
      "countryCode": "DE"
    }
  },
  "lines": [
    {
      "unitCode": "C62",
      "itemClassifications": [
        {
          "schemeId": "TST"
        }
      ]
    }
  ],
  "payment": {
    "meansCode": "58"
  },
  "declaredTotals": {
    "defects": [
      {
        "field": "BT-115"
      }
    ],
    "overPrecise": [
      {
        "field": "BT-131"
      }
    ]
  }
}

Response

The generated document.

xmlstring required

The complete XML document.

profile'en16931' | 'xrechnung-ubl' | 'peppol-bis-3' | 'xrechnung-cii' | 'facturx-en16931' required
syntax'ubl' | 'cii' required

Which syntax the xml is in. Determined by profile: xrechnung-cii and facturx-en16931 are CII, everything else is UBL 2.1.

notestring

Present on CII responses only. States that the document is the CII XML payload and not a Factur-X or ZUGFeRD PDF, because that is the mistake this response invites. Absent for UBL.

Example response

{
  "warnings": [
    {
      "rule": "BR-DE-15",
      "field": "BT-10",
      "location": {
        "attachment": "factur-x.xml"
      }
    }
  ],
  "information": [
    {
      "rule": "BR-DE-15",
      "field": "BT-10",
      "location": {
        "attachment": "factur-x.xml"
      }
    }
  ],
  "findings_truncated": {
    "total": 20012,
    "returned": 500,
    "cap": 500
  },
  "provenance": {
    "engine": "@attestwire/en16931",
    "engine_version": "0.14.1",
    "ruleset": "en16931@0.14.1",
    "profile": "xrechnung-ubl",
    "kosit_conformance": {
      "recorded": "2026-09-24",
      "validator": "1.6.3",
      "configuration": "3.0.2",
      "configuration_published": "2026-08-31"
    }
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.