<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: PatrickPi1312</title>
    <description>The latest articles on DEV Community by PatrickPi1312 (@patrickpi1312).</description>
    <link>https://dev.to/patrickpi1312</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4025975%2F54fd86da-1877-4b46-997c-eb49ac8928f4.png</url>
      <title>DEV Community: PatrickPi1312</title>
      <link>https://dev.to/patrickpi1312</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/patrickpi1312"/>
    <language>en</language>
    <item>
      <title>XRechnung aus PDF erstellen: Anleitung und API-Beispiel (2026)</title>
      <dc:creator>PatrickPi1312</dc:creator>
      <pubDate>Sun, 12 Jul 2026 12:47:28 +0000</pubDate>
      <link>https://dev.to/patrickpi1312/xrechnung-aus-pdf-erstellen-anleitung-und-api-beispiel-2026-3e26</link>
      <guid>https://dev.to/patrickpi1312/xrechnung-aus-pdf-erstellen-anleitung-und-api-beispiel-2026-3e26</guid>
      <description>&lt;p&gt;Die E-Rechnungs-Pflicht ist da: In Deutschland müssen Unternehmen seit 2025 E-Rechnungen empfangen können, ab 2027/2028 auch ausstellen. In Österreich läuft e-Rechnung über ebInterface und Peppol. Das Problem für viele Betriebe: Die alte Software erzeugt nur ein &lt;strong&gt;PDF&lt;/strong&gt; — verlangt wird aber eine strukturierte &lt;strong&gt;XRechnung&lt;/strong&gt; oder &lt;strong&gt;ZUGFeRD&lt;/strong&gt;. So schließt du die Lücke per API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schritt 1: PDF in strukturierte Daten umwandeln
&lt;/h2&gt;

&lt;p&gt;Die Extraction-API liest ein PDF (oder Foto) und gibt Händler, Datum, Beträge und Positionen als JSON zurück:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://extract.installateur1210.at/v1/extract"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: DEIN_KEY"&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@rechnung.pdf"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Da es KI-gestützt ist: &lt;strong&gt;Ergebnis kurz prüfen&lt;/strong&gt;, bevor du es weiterverwendest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schritt 2: Daten in eine gültige XRechnung wandeln
&lt;/h2&gt;

&lt;p&gt;Die geprüften Felder gehen in den Generator — heraus kommt eine EN-16931-konforme XRechnung. Profil wählbar: &lt;code&gt;xrechnung&lt;/code&gt; (DE), &lt;code&gt;peppol&lt;/code&gt; (EU/Österreich), &lt;code&gt;en16931&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://einvoice.installateur1210.at/v1/einvoice/generate?key=demo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"invoice_number":"2026-001","issue_date":"2026-07-12","currency":"EUR",
       "seller":"Muster GmbH","seller_country":"AT","seller_vat":"ATU12345678",
       "buyer":"Kunde GmbH","buyer_country":"DE",
       "total_net":100,"tax_amount":20,"total_gross":120,
       "line_items":[{"name":"Leistung","quantity":1,"line_amount":100}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Schritt 3: Amtlich prüfen
&lt;/h2&gt;

&lt;p&gt;Das ist der Teil, den selbstgebaute Lösungen meist auslassen. Prüfe die XRechnung gegen den &lt;strong&gt;offiziellen KoSIT-Validator&lt;/strong&gt; (EN 16931 + XRechnung-Schematron):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://einvoice.installateur1210.at/v1/einvoice/validate/official?key=demo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/xml"&lt;/span&gt; &lt;span class="nt"&gt;--data-binary&lt;/span&gt; @rechnung.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Du bekommst ein Urteil (ACCEPTABLE/REJECTED) plus die konkret verletzten Geschäftsregeln (BR-CO-15, BR-DE-…).&lt;/p&gt;

&lt;h2&gt;
  
  
  Fazit
&lt;/h2&gt;

&lt;p&gt;Aus „alte Software macht nur PDF" wird per Pipeline &lt;strong&gt;PDF → JSON → XRechnung&lt;/strong&gt; — für Deutschland, Österreich und EU-weit über Peppol. EU-gehostet, keine Rechnungsdaten gespeichert, kostenloser Demo-Key &lt;code&gt;demo&lt;/code&gt;. Beispiele: &lt;a href="https://github.com/PatrickPi1312/xrechnung-api-examples" rel="noopener noreferrer"&gt;https://github.com/PatrickPi1312/xrechnung-api-examples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Keine Steuer- oder Rechtsberatung. Welches Format bereitet dir Probleme?&lt;/p&gt;

</description>
      <category>einvoicing</category>
      <category>api</category>
      <category>tutorial</category>
      <category>germany</category>
    </item>
    <item>
      <title>Verify emails at signup: MX, disposable and typo detection in one API call</title>
      <dc:creator>PatrickPi1312</dc:creator>
      <pubDate>Sun, 12 Jul 2026 12:37:32 +0000</pubDate>
      <link>https://dev.to/patrickpi1312/verify-emails-at-signup-mx-disposable-and-typo-detection-in-one-api-call-15n9</link>
      <guid>https://dev.to/patrickpi1312/verify-emails-at-signup-mx-disposable-and-typo-detection-in-one-api-call-15n9</guid>
      <description>&lt;p&gt;A wrong or fake email at signup costs you later: bounced invoices, failed password resets, junk in your database, a hurt sender reputation. Catching it &lt;strong&gt;at the moment of signup&lt;/strong&gt; is cheap. Here's a one-call check.&lt;/p&gt;

&lt;h2&gt;
  
  
  The call
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://email.installateur1210.at/v1/email/verify?key=demo&amp;amp;email=ceo@stripe.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ceo@stripe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"valid_syntax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"domain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stripe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"has_mx"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="c1"&gt;// the domain can actually receive mail (live DNS)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_disposable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;// not a throwaway domain&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"did_you_mean"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;// typo suggestion, e.g. "gmial.com" -&amp;gt; "gmail.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deliverable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"score"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Catch typos before they cost you
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;did_you_mean&lt;/code&gt; field is the quiet hero. A user typing &lt;code&gt;john@gmial.com&lt;/code&gt; never gets your email — with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://email.installateur1210.at/v1/email/verify?key=demo&amp;amp;email=john@gmial.com"&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; "did_you_mean": "john@gmail.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…you can prompt "did you mean gmail.com?" right in the form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wire it into a form (JS)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://email.installateur1210.at/v1/email/verify?key=demo&amp;amp;email=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deliverable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;showWarning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This email doesn't look reachable.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;did_you_mean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;showHint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Did you mean &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;did_you_mean&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Free demo key &lt;code&gt;demo&lt;/code&gt; (50/day), EU-hosted, nothing stored. Good for signups, newsletters and checkout.&lt;/p&gt;

</description>
      <category>api</category>
      <category>tutorial</category>
      <category>webdev</category>
      <category>node</category>
    </item>
    <item>
      <title>Turn a PDF invoice into structured JSON — then into a valid XRechnung</title>
      <dc:creator>PatrickPi1312</dc:creator>
      <pubDate>Sun, 12 Jul 2026 12:37:27 +0000</pubDate>
      <link>https://dev.to/patrickpi1312/turn-a-pdf-invoice-into-structured-json-then-into-a-valid-xrechnung-3oa2</link>
      <guid>https://dev.to/patrickpi1312/turn-a-pdf-invoice-into-structured-json-then-into-a-valid-xrechnung-3oa2</guid>
      <description>&lt;p&gt;Here's a problem millions of small businesses hit in 2026: their old software only prints &lt;strong&gt;PDF invoices&lt;/strong&gt;, but the law increasingly requires a &lt;strong&gt;structured e-invoice&lt;/strong&gt; (XRechnung, ZUGFeRD, Peppol). You can bridge that gap in two API calls: extract the PDF into data, then generate a compliant e-invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — PDF (or photo) → structured JSON
&lt;/h2&gt;

&lt;p&gt;Send the document to the extraction endpoint and get back the fields — merchant, date, currency, totals and line items:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://extract.installateur1210.at/v1/extract"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@invoice.pdf"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"merchant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ACME GmbH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EUR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subtotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;100.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tax"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;20.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;120.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"line_items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Consulting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;100.00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's AI-based, so &lt;strong&gt;review the result&lt;/strong&gt; before you rely on it — layouts vary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — JSON → valid XRechnung / Peppol
&lt;/h2&gt;

&lt;p&gt;Feed those fields into the e-invoice generator and get a compliant EN 16931 XML (validated ACCEPTABLE by the official KoSIT validator):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://einvoice.installateur1210.at/v1/einvoice/generate?key=demo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"invoice_number":"AT-2026-001","issue_date":"2026-07-12","currency":"EUR",
       "seller":"ACME GmbH","seller_country":"AT",
       "buyer":"Client Ltd","buyer_country":"DE",
       "total_net":100,"tax_amount":20,"total_gross":120,
       "line_items":[{"name":"Consulting","quantity":1,"line_amount":100}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The whole point
&lt;/h2&gt;

&lt;p&gt;Old PDF-only billing tool on one end, EU e-invoicing mandate on the other — and a two-step pipeline in between: &lt;strong&gt;PDF → JSON → XRechnung&lt;/strong&gt;. Both APIs are EU-hosted and store no document data. Extraction needs its own key (free tier available); the e-invoice API has a public demo key &lt;code&gt;demo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Examples: &lt;a href="https://github.com/PatrickPi1312/xrechnung-api-examples" rel="noopener noreferrer"&gt;https://github.com/PatrickPi1312/xrechnung-api-examples&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>tutorial</category>
      <category>einvoicing</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>How to create a valid XRechnung (EN 16931) e-invoice in 5 minutes</title>
      <dc:creator>PatrickPi1312</dc:creator>
      <pubDate>Sun, 12 Jul 2026 12:29:37 +0000</pubDate>
      <link>https://dev.to/patrickpi1312/how-to-create-a-valid-xrechnung-en-16931-e-invoice-in-5-minutes-gkl</link>
      <guid>https://dev.to/patrickpi1312/how-to-create-a-valid-xrechnung-en-16931-e-invoice-in-5-minutes-gkl</guid>
      <description>&lt;p&gt;E-invoicing is now mandatory across much of the EU (Germany B2B from 2025, Austria e-Rechnung, Peppol EU-wide), and a lot of software suddenly needs to emit a &lt;strong&gt;valid XRechnung / Peppol invoice&lt;/strong&gt; — not a PDF. Here's the shortest path I've found, without becoming an EN 16931 expert.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start from a neutral JSON
&lt;/h2&gt;

&lt;p&gt;You don't hand-write UBL. You describe the invoice in plain JSON and let the API produce the XML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://einvoice.installateur1210.at/v1/einvoice/generate?key=demo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "invoice_number": "AT-2026-001",
    "issue_date": "2026-07-12",
    "currency": "EUR",
    "seller": "My Company GmbH", "seller_country": "AT", "seller_vat": "ATU12345678",
    "buyer": "Client Ltd", "buyer_country": "DE",
    "total_net": 100, "tax_amount": 20, "total_gross": 120, "vat_rate": 20,
    "line_items": [{ "name": "Consulting", "quantity": 1, "line_amount": 100 }]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;profile&lt;/code&gt; field switches the target: &lt;code&gt;peppol&lt;/code&gt; (EU-wide, incl. Austria — the default), &lt;code&gt;xrechnung&lt;/code&gt; (Germany) or &lt;code&gt;en16931&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Prove it's actually compliant
&lt;/h2&gt;

&lt;p&gt;This is the part most home-grown solutions skip. The output above is validated &lt;strong&gt;ACCEPTABLE by the official German KoSIT validator&lt;/strong&gt; (EN 16931 + XRechnung Schematron). You can check any invoice yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://einvoice.installateur1210.at/v1/einvoice/validate/official?key=demo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/xml"&lt;/span&gt; &lt;span class="nt"&gt;--data-binary&lt;/span&gt; @invoice.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns a verdict plus the exact failed business rules (BR-CO-15, BR-DE-…), so you know &lt;em&gt;why&lt;/em&gt; something is rejected.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Read incoming invoices too — including ZUGFeRD PDFs
&lt;/h2&gt;

&lt;p&gt;Incoming invoices arrive in many shapes. Parse XRechnung (UBL), ZUGFeRD (CII) or Austrian ebInterface into one clean JSON — and yes, you can throw a &lt;strong&gt;ZUGFeRD PDF&lt;/strong&gt; straight in; the embedded XML is extracted for you:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://einvoice.installateur1210.at/v1/einvoice/parse?key=demo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/pdf"&lt;/span&gt; &lt;span class="nt"&gt;--data-binary&lt;/span&gt; @zugferd.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Convert between formats
&lt;/h2&gt;

&lt;p&gt;Got an Austrian ebInterface file but need Peppol? One call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://einvoice.installateur1210.at/v1/einvoice/convert?to=peppol&amp;amp;key=demo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/xml"&lt;/span&gt; &lt;span class="nt"&gt;--data-binary&lt;/span&gt; @ebinterface.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;Free demo key &lt;code&gt;demo&lt;/code&gt; (50/day), EU-hosted, no invoice data stored. Full code examples (Node, Python, PHP): &lt;a href="https://github.com/PatrickPi1312/xrechnung-api-examples" rel="noopener noreferrer"&gt;https://github.com/PatrickPi1312/xrechnung-api-examples&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Not legal or tax advice — but a fast way to stop wrestling with EN 16931 by hand. What format is giving you trouble?&lt;/p&gt;

</description>
      <category>einvoicing</category>
      <category>api</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Add a scan-to-pay SEPA QR code (EPC/Girocode) to your invoices</title>
      <dc:creator>PatrickPi1312</dc:creator>
      <pubDate>Sun, 12 Jul 2026 12:29:13 +0000</pubDate>
      <link>https://dev.to/patrickpi1312/add-a-scan-to-pay-sepa-qr-code-epcgirocode-to-your-invoices-hb2</link>
      <guid>https://dev.to/patrickpi1312/add-a-scan-to-pay-sepa-qr-code-epcgirocode-to-your-invoices-hb2</guid>
      <description>&lt;p&gt;Across the SEPA area, banking apps can scan an &lt;strong&gt;EPC QR code (a.k.a. Girocode)&lt;/strong&gt; to pre-fill a bank transfer — recipient, IBAN, amount, reference. Putting one on your invoices or payment pages measurably speeds up getting paid. Here's how to generate one with a single request.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-liner
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.installateur1210.at/v1/epc-qr?key=demo&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
&amp;amp;name=ACME%20GmbH&amp;amp;iban=DE89370400440532013000&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
&amp;amp;amount=99.50&amp;amp;remittance=Invoice%202026-001"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; qr.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a spec-compliant EPC069-12 QR as PNG (or &lt;code&gt;format=svg&lt;/code&gt;). The IBAN is validated server-side (mod-97), so you get a clear error instead of a broken code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Embed it directly in HTML
&lt;/h2&gt;

&lt;p&gt;No backend needed — it's just an image URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Pay by bank transfer"&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://api.installateur1210.at/v1/epc-qr?key=demo&amp;amp;name=ACME%20GmbH&amp;amp;iban=DE89370400440532013000&amp;amp;amount=99.50&amp;amp;remittance=Invoice%202026-001"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Node.js
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;demo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ACME GmbH&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;iban&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DE89370400440532013000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;99.50&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;remittance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invoice 2026-001&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.installateur1210.at/v1/epc-qr?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;qr.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Validate an IBAN separately
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.installateur1210.at/v1/iban/DE89370400440532013000?key=demo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Free demo key &lt;code&gt;demo&lt;/code&gt;, EU-hosted, nothing stored. Handy for invoicing tools, clubs, utilities and checkout flows in the euro area.&lt;/p&gt;

</description>
      <category>api</category>
      <category>fintech</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>EU e-invoicing for developers: XRechnung vs ZUGFeRD vs ebInterface vs Peppol</title>
      <dc:creator>PatrickPi1312</dc:creator>
      <pubDate>Sun, 12 Jul 2026 12:07:42 +0000</pubDate>
      <link>https://dev.to/patrickpi1312/eu-e-invoicing-for-developers-xrechnung-vs-zugferd-vs-ebinterface-vs-peppol-51fc</link>
      <guid>https://dev.to/patrickpi1312/eu-e-invoicing-for-developers-xrechnung-vs-zugferd-vs-ebinterface-vs-peppol-51fc</guid>
      <description>&lt;p&gt;If you build accounting, ERP, shop or billing software and sell into Europe, e-invoicing has probably landed on your roadmap whether you wanted it or not. Germany's B2B mandate, Austria's &lt;code&gt;e-Rechnung&lt;/code&gt;, and the EU-wide Peppol push all point the same way: &lt;strong&gt;structured e-invoices are becoming the default, and PDFs-by-email are on the way out.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The confusing part isn't the concept. It's that "e-invoice" means four or five different things depending on the country and the day. This post is the map I wish I'd had.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "e-invoice" actually means
&lt;/h2&gt;

&lt;p&gt;An e-invoice is &lt;strong&gt;not&lt;/strong&gt; a PDF. A PDF is a picture of an invoice for humans. An e-invoice is &lt;em&gt;structured data&lt;/em&gt; a machine can read without OCR or guesswork. That distinction is the whole point of the regulation: automated processing, fewer errors, less fraud.&lt;/p&gt;

&lt;p&gt;The good news: underneath all the acronyms there is &lt;strong&gt;one shared model&lt;/strong&gt; — the European standard &lt;strong&gt;EN 16931&lt;/strong&gt;. It defines the &lt;em&gt;semantics&lt;/em&gt;: what fields an invoice must contain (seller, buyer, invoice number, dates, line items, VAT breakdown, totals) and business rules between them (e.g. &lt;code&gt;grand total = net + tax&lt;/code&gt;). Everything below is a flavour of the same core.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two syntaxes
&lt;/h2&gt;

&lt;p&gt;EN 16931 can be expressed in two XML syntaxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UBL 2.1&lt;/strong&gt; (OASIS Universal Business Language) — the more common one, used by Peppol and XRechnung.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UN/CEFACT CII&lt;/strong&gt; (Cross Industry Invoice) — used inside ZUGFeRD/Factur-X.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same information, different element names. In UBL the invoice number is &lt;code&gt;cbc:ID&lt;/code&gt;; in CII it's &lt;code&gt;ram:ID&lt;/code&gt; inside &lt;code&gt;rsm:ExchangedDocument&lt;/code&gt;. If you parse by &lt;em&gt;local element name&lt;/em&gt; and map to a neutral model, you can support both with one code path — more on that below.&lt;/p&gt;

&lt;h2&gt;
  
  
  The format zoo
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;th&gt;Syntax&lt;/th&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;XRechnung&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Germany 🇩🇪&lt;/td&gt;
&lt;td&gt;UBL or CII&lt;/td&gt;
&lt;td&gt;Pure XML. A German CIUS (national profile) of EN 16931, maintained by KoSIT.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ZUGFeRD / Factur-X&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Germany 🇩🇪 / France 🇫🇷&lt;/td&gt;
&lt;td&gt;CII&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Hybrid&lt;/strong&gt;: a PDF/A-3 with the XML &lt;em&gt;embedded inside the PDF&lt;/em&gt;. Human-readable and machine-readable in one file.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ebInterface&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Austria 🇦🇹&lt;/td&gt;
&lt;td&gt;own XML schema&lt;/td&gt;
&lt;td&gt;Austria's national standard (maintained by AUSTRIAPRO), predates EN 16931. Still widely used in AT.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Peppol BIS Billing 3.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;EU-wide 🇪🇺&lt;/td&gt;
&lt;td&gt;UBL&lt;/td&gt;
&lt;td&gt;Not a file format so much as an &lt;em&gt;interoperability profile&lt;/em&gt; transported over the Peppol network. The de-facto EU lingua franca.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few things that trip people up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;XRechnung is not a file type, it's a profile.&lt;/strong&gt; An XRechnung document can be UBL &lt;em&gt;or&lt;/em&gt; CII. Don't assume the syntax from the name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ZUGFeRD is a PDF.&lt;/strong&gt; The structured data lives in an embedded XML attachment (CII). To process it you first extract that XML, then treat it like any CII invoice. ZUGFeRD even has an &lt;code&gt;XRECHNUNG&lt;/code&gt; profile so one file can satisfy both worlds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ebInterface is its own thing.&lt;/strong&gt; Austrian invoices you receive may well be ebInterface, not EN 16931. If your parser only knows UBL/CII, it will silently choke on AT invoices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Peppol is the network, BIS Billing 3.0 is what flows over it.&lt;/strong&gt; You send via an Access Point using the 4-corner model; the payload is EN 16931 UBL.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the deadlines matter (the short version)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Germany:&lt;/strong&gt; since &lt;strong&gt;1 Jan 2025&lt;/strong&gt;, every B2B business must be &lt;em&gt;able to receive&lt;/em&gt; EN 16931 e-invoices. &lt;em&gt;Issuing&lt;/em&gt; becomes mandatory for larger companies from &lt;strong&gt;2027&lt;/strong&gt; and for everyone from &lt;strong&gt;2028&lt;/strong&gt;. Accepted formats: XRechnung and ZUGFeRD (both EN 16931-compliant).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Austria:&lt;/strong&gt; B2G has required structured invoices for years (ebInterface or Peppol via USP / e-rechnung.gv.at).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EU (ViDA):&lt;/strong&gt; the "VAT in the Digital Age" package pushes structured e-invoicing and digital reporting across the bloc over the coming years.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Translation for us: a lot of software &lt;em&gt;suddenly&lt;/em&gt; needs to read and write these formats, and most teams don't want to become EN 16931 experts to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing it in code
&lt;/h2&gt;

&lt;p&gt;The pragmatic approach for reading invoices is to &lt;strong&gt;normalise on parse&lt;/strong&gt;: walk the XML by local element name, detect the syntax, and emit one neutral JSON shape regardless of whether the source was UBL, CII or ebInterface. Here's the idea in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;xml.etree.ElementTree&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ET&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# strip namespace
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;detect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;local&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invoice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ebinterface&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ebInterface (AT)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Invoice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UBL (XRechnung/Peppol)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CrossIndustryInvoice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CII (ZUGFeRD)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there you collect the fields you care about across the syntaxes (invoice number, dates, seller/buyer, VAT, totals, lines), validate the EN 16931 required fields, and check the arithmetic. Generating is the reverse: take a neutral JSON object and emit valid UBL with the right &lt;code&gt;CustomizationID&lt;/code&gt;/&lt;code&gt;ProfileID&lt;/code&gt; for your target profile (Peppol for EU/AT, XRechnung for DE).&lt;/p&gt;

&lt;p&gt;If you'd rather not build and maintain all of that, I put the whole thing behind a small API while solving it for my own trades business in Vienna. Free demo key &lt;code&gt;demo&lt;/code&gt;, EU-hosted, no data stored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Generate a Peppol/EU invoice (works for Austria too)&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://einvoice.installateur1210.at/v1/einvoice/generate?key=demo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "invoice_number": "AT-2026-001",
    "issue_date": "2026-07-12",
    "currency": "EUR",
    "seller": "My Company GmbH", "seller_country": "AT", "seller_vat": "ATU12345678",
    "buyer": "Client Ltd", "buyer_country": "DE",
    "total_net": 100, "tax_amount": 20, "total_gross": 120, "vat_rate": 20,
    "line_items": [{"name": "Consulting", "quantity": 1, "line_amount": 100}]
  }'&lt;/span&gt;

&lt;span class="c"&gt;# Validate an incoming XML against EN 16931 (UBL, CII or ebInterface)&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://einvoice.installateur1210.at/v1/einvoice/validate?key=demo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/xml"&lt;/span&gt; &lt;span class="nt"&gt;--data-binary&lt;/span&gt; @incoming-invoice.xml

&lt;span class="c"&gt;# Parse any of the formats into clean JSON&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://einvoice.installateur1210.at/v1/einvoice/parse?key=demo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/xml"&lt;/span&gt; &lt;span class="nt"&gt;--data-binary&lt;/span&gt; @incoming-invoice.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;generate&lt;/code&gt; endpoint takes a &lt;code&gt;profile&lt;/code&gt; (&lt;code&gt;peppol&lt;/code&gt; for EU/Austria, &lt;code&gt;xrechnung&lt;/code&gt; for Germany, or plain &lt;code&gt;en16931&lt;/code&gt;), so the same JSON can target different countries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas worth knowing before you ship
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Don't infer syntax from the format name.&lt;/strong&gt; Detect it from the XML root/namespace.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ZUGFeRD = extract the embedded XML first.&lt;/strong&gt; The PDF wrapper is not the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Austria will send you ebInterface.&lt;/strong&gt; Handle it or you'll drop real invoices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate the maths, not just the schema.&lt;/strong&gt; A schema-valid invoice can still have &lt;code&gt;net + tax ≠ gross&lt;/code&gt;; EN 16931 business rules catch that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Currency and country codes are required fields&lt;/strong&gt;, and buyers increasingly need a valid VAT ID and buyer reference — don't leave them blank.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;E-invoicing looks intimidating from the outside, but it's really one semantic model wearing several national costumes. Map the costumes once and the rest is plumbing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Which country's format is giving you the most trouble? I'm actively adding coverage — happy to hear what to support next.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>einvoicing</category>
      <category>api</category>
      <category>dotnet</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
