Skip to main content

ZUGFeRD E-Invoice from a SharePoint List: Turn JSON into EN 16931 XML and a PDF/A-3 in Power Automate with PDF4me

· 29 min read
SEO and Content Writer

PDF4me Create ZUGFeRD Invoice is a Power Automate action that turns invoice data into a hybrid PDF/A-3 e-invoice. Pointed at a SharePoint list, it takes the JSON a Compose action builds from each row, writes the EN 16931 XML itself, draws the readable invoice onto your base PDF, files the result and emails it to the buyer.

Look up ZUGFeRD and you land in one of three places: an explanation of what the format is, a validator that tells you whether a file you already have is correct, or a library in PHP, Java or C# for developers building invoicing software. All three assume the invoice already exists somewhere. The question none of them answers is the one a finance team actually has: the invoice data is sitting in a list, in a spreadsheet, in whatever system records what was sold, and it has to come out the other end as a compliant e-invoice. This flow is that missing middle, in seven actions and without code.

The flow at a glance
1. When an item is created
SharePoint trigger on the list named ZUGFeRD Requests. One row is one invoice.
2. Compose
Builds one invoice JSON object from the row: seller, buyer, line item, totals, payment.
3. Get file content using path
Reads BaseInvoice.pdf, the page the invoice is drawn on.
4. Create ZUGFeRD Invoice
PDF4me PDF. Input format 2.0-JSON, XmlWithPdf, conformance EN16931, invoice rendered.
5. Create file
Saves the hybrid PDF into the ZUGFeRD Invoices library.
6. Update item
Stamps Status Generated and the OutputLink back on the same row.
7. Send an email (V2)
Mails the invoice to the buyer address on the row.
The short version

A row lands in a SharePoint list with the seller, the buyer, one line item and the dates. Four seconds later there is a PDF/A-3 invoice in a document library: a page a person can read, with the EN 16931 XML embedded inside it for the buyer's software to process. You never write that XML. You describe the invoice as a short JSON object and PDF4me produces the schema. The row gets the status and a link, and the buyer gets the file by email.

Why-Based Q&A​

Why keep the invoice data in a SharePoint list? Because that is the format the data is already in for most small finance teams: a row per invoice, filled in by a person. The columns map one to one onto the fields the standard asks for, which means the mapping is visible to anyone who opens the list, and a new invoice is a new row rather than a new file in a watched folder.

Why describe the invoice as JSON instead of writing the XML? Because the Cross Industry Invoice syntax is verbose and strict: nested namespaces, element names nobody remembers, codes that have to sit in exactly the right place. The JSON object says the same thing in a fraction of the lines, with keys that read like the business terms they stand for. PDF4me converts it to the schema, so the part that is easy to get wrong is the part you do not maintain.

Why render the invoice onto a base PDF? Because a ZUGFeRD file has two halves and both have to agree. The XML is for the buyer's software, the printed page is for the person who opens it. Render Invoice on PDF draws the invoice onto the base page from the same data that produced the XML, so the two can never drift apart the way they do when a layout is maintained separately.

Why PDF/A-3 and not an ordinary PDF? Because PDF/A-3 is the archival container that is allowed to carry an attachment, and the embedded XML is that attachment. It is also the part that makes the invoice readable years later: fonts and colour profiles travel inside the file, which matters for a document most companies are required to keep for a decade.


What You'll Get​

Input: a row with the invoice number, the dates, the currency, the seller block, the buyer block and one line item. Output: a ZUGFeRD PDF/A-3 invoice in the ZUGFeRD Invoices library, the row updated with its status and a link, and an email to the buyer with the invoice attached.

SharePoint list item RE-2026-0007 in ZUGFeRD Requests showing InvoiceNumber RE-2026-0007, InvoiceDate 12.10.2026, DueDate 11.11.2026, Currency EUR, SellerName Muster GmbH, SellerStreet Hauptstrasse 1, SellerPostalCode 10115, SellerCity Berlin, SellerCountry DE, SellerVATID DE123456789, BuyerName Test Kunde AG, BuyerStreet Bahnhofstrasse 5, BuyerPostalCode 80331, BuyerCity Muenchen, BuyerCountry DE, ItemDescription Consulting, Quantity 10,00, UnitPrice 100,50, VATRate 19, an Email column and Status Generated

One row per invoice. Every column in this pane becomes a key in the JSON the flow builds.

SharePoint ZUGFeRD Invoices library listing ZUGFeRD_RE-2026-0001.pdf through ZUGFeRD_RE-2026-0004.pdf and the newest ZUGFeRD_RE-2026-0007.pdf, all modified by the Pdf4me Flow account

One file per invoice number, written by the flow account.

Outlook showing the email with ZUGFeRD_RE-2026-0007.pdf attached and the rendered German invoice open in the preview pane: seller Muster GmbH in Berlin with VAT ID DE123456789, customer Test Kunde AG in Muenchen, invoice RE-2026-0007 dated 12.10.2026, document type 380, one line of Consulting at 19 percent VAT, net 1.005,00 euro and total 1.195,95 euro

The readable half of the same file, as the buyer sees it.


What You Need​

  • Power Automate. Open Power Automate. An automated cloud flow on standard connectors. SharePoint, Office 365 Outlook and PDF4me need no premium plan.
  • PDF4me API key. Get your API key. This flow uses one PDF4me connection, PDF4me PDF.
  • A SharePoint list for the requests. The captured list is called ZUGFeRD Requests, with InvoiceNumber, InvoiceDate, DueDate, Currency, the Seller block (name, street, postal code, city, country, VAT ID), the Buyer block, ItemDescription, Quantity, UnitPrice, VATRate, an Email column, a Status choice column and an OutputLink column the flow writes to.
  • A base invoice PDF. The captured flow keeps BaseInvoice.pdf in a library called ZUGFeRD Templates. This is the page the invoice is drawn on, so your letterhead belongs here.
  • A library for the finished invoices. The captured flow writes into ZUGFeRD Invoices.
  • Your own seller details, including a VAT ID. They go into the list columns, so the same flow can issue invoices for more than one legal entity.
Start from a working JSON object
If you would rather see the shape before you build the Compose, this is the full ZUGFeRD 2.0 EN16931 structure with every key the profile accepts. Paste it into Compose and swap the values for your tokens.
Download sample-invoice-zugferd-2.0-en16931.json

The Flow at a Glance​

  1. When an item is created (SharePoint) watches the list named ZUGFeRD Requests.
  2. Compose builds the invoice JSON from the row.
  3. Get file content using path reads /ZUGFeRD Templates/BaseInvoice.pdf.
  4. Create ZUGFeRD Invoice (PDF4me PDF) converts the JSON to EN 16931 XML and returns the hybrid PDF.
  5. Create file (SharePoint) saves it as ZUGFeRD_<invoice number>.pdf.
  6. Update item (SharePoint) sets Status to Generated and fills OutputLink.
  7. Send an email (V2) (Office 365 Outlook) sends the invoice to the buyer.

Complete flow overview​

Power Automate run history showing seven actions succeeding in order: When an item is created at 0.2 seconds, Compose at 0 seconds, Get file content using path at 0.2 seconds, Create ZUGFeRD Invoice at 1 second, Create file at 2 seconds, Update item at 0.4 seconds and Send an email V2 at 0.4 seconds

Four and a bit seconds end to end, and the e-invoice itself takes about a second. The slowest step is SharePoint writing the file.


Step 1: How do you trigger an e-invoice when a request arrives?​

Flow so far: nothing yet, this is the trigger.

Adding a row is how an invoice gets requested, so that is the signal.

  1. Create an Automated cloud flow and pick SharePoint > When an item is created.
  2. Configure:
    • Site Address: PDF4me Support - https://ynoox1.sharepoint.com/sites/PDF4meSupport
    • List Name: ZUGFeRD Requests

When an item is created configuration​

SharePoint When an item is created trigger with Site Address PDF4me Support at ynoox1.sharepoint.com/sites/PDF4meSupport and List Name ZUGFeRD Requests, with no advanced parameters set

Every column on the new row becomes a token the next six actions can use.


Step 2: How do you build the invoice JSON from list columns?​

Flow so far: the SharePoint trigger.

One Compose action describes the whole invoice. This is where your column names meet the names the standard expects, and it is short enough to read on one screen.

  1. Add Data Operation > Compose.
  2. Paste the object below into Inputs and replace each value with the matching token.
{
"InvoiceId": "@{triggerBody()?['InvoiceNumber']}",
"TypeCode": "380",
"InvoiceType": "RECHNUNG",
"IssueDate": "@{triggerBody()?['InvoiceDate']}T00:00:00",
"DeliveryDate": "@{triggerBody()?['InvoiceDate']}T00:00:00",
"IsTest": false,
"Seller": {
"Name": "@{triggerBody()?['SellerName']}",
"VatId": "@{triggerBody()?['SellerVATID']}",
"Address": {
"Street": "@{triggerBody()?['SellerStreet']}",
"PostalCode": "@{triggerBody()?['SellerPostalCode']}",
"City": "@{triggerBody()?['SellerCity']}",
"Country": "@{triggerBody()?['SellerCountry']}"
}
},
"Buyer": {
"Name": "@{triggerBody()?['BuyerName']}",
"Address": {
"Street": "@{triggerBody()?['BuyerStreet']}",
"PostalCode": "@{triggerBody()?['BuyerPostalCode']}",
"City": "@{triggerBody()?['BuyerCity']}",
"Country": "@{triggerBody()?['BuyerCountry']}"
}
},
"LineItems": [
{
"ProductName": "@{triggerBody()?['ItemDescription']}",
"Quantity": @{triggerBody()?['Quantity']},
"UnitCode": "C62",
"UnitPrice": @{triggerBody()?['UnitPrice']},
"LineTotal": @{float(...)}
}
],
"Totals": {
"Currency": "@{triggerBody()?['Currency']?['Value']}",
"LineTotal": @{float(...)},
"TaxBasisTotal": @{float(...)},
"TaxPercent": @{triggerBody()?['VATRate']},
"TaxTotal": @{float(...)},
"GrandTotal": @{float(...)},
"ChargeTotal": 0,
"AllowanceTotal": 0
},
"Payment": {
"Currency": "@{triggerBody()?['Currency']?['Value']}",
"PaymentReference": "@{triggerBody()?['InvoiceNumber']}",
"DueDate": "@{triggerBody()?['DueDate']}T00:00:00"
}
}

Compose configuration​

Power Automate Compose action holding the invoice JSON with InvoiceId, TypeCode 380, InvoiceType RECHNUNG, IssueDate and DeliveryDate, a Seller object with VatId and address, a Buyer object with address, a LineItems array with ProductName, Quantity, UnitCode C62 and UnitPrice, a Totals object with Currency, TaxPercent and float expressions for the money values, and a Payment object with the payment reference and due date

Text values are tokens in quotes. Money and quantities are bare numbers, which is why they go through float.

Where each part of the invoice comes from​

JSON keyWhat it carriesSource
InvoiceId, TypeCode, InvoiceTypeInvoice number, the document type code 380 for a commercial invoice, and the label printed on the pageInvoiceNumber, then fixed values
IssueDate, DeliveryDateWhen the invoice was issued and when the service was deliveredInvoiceDate, suffixed with T00:00:00
SellerName, VAT ID and the full address blockSellerName, SellerVATID, SellerStreet, SellerPostalCode, SellerCity, SellerCountry
BuyerName and address of the customerBuyerName, BuyerStreet, BuyerPostalCode, BuyerCity, BuyerCountry
LineItemsDescription, quantity with unit code C62, unit price and the line totalItemDescription, Quantity, UnitPrice
TotalsCurrency, net total, tax basis, VAT rate and amount, grand totalCurrency, VATRate and the computed amounts
PaymentCurrency, the reference the buyer quotes when paying, and the due dateCurrency, InvoiceNumber, DueDate

Tip. The money keys are the only part that needs arithmetic, and they must end up as bare numbers rather than quoted strings, which is what the float expressions are for. LineTotal is quantity times unit price, TaxBasisTotal is the sum of the line totals, TaxTotal is the tax basis times the rate divided by 100, and GrandTotal is the two added together. For the captured row that is 1005, 1005, 190.95 and 1195.95. A quoted "1005" will be rejected where a plain 1005 is accepted.


Step 3: Where does the base PDF come from?​

Flow so far: the trigger plus Compose.

ZUGFeRD files have a readable page, and this is where it starts.

  1. Add SharePoint > Get file content using path.
  2. Configure:
    • Site Address: PDF4me Support - https://ynoox1.sharepoint.com/sites/PDF4meSupport
    • File Path: /ZUGFeRD Templates/BaseInvoice.pdf
    • Infer content type (advanced): Yes

Get file content using path configuration​

SharePoint Get file content using path action with Site Address PDF4me Support, File Path /ZUGFeRD Templates/BaseInvoice.pdf and Infer content type set to Yes

Keep your letterhead in this file. It is read fresh on every run, so changing it changes the next invoice.


Step 4: How do you turn the JSON into a ZUGFeRD PDF/A-3?​

Flow so far: the trigger, Compose and Get file content using path.

This is the action that produces the e-invoice. It reads the JSON, writes the EN 16931 XML itself, and hands back one file with both halves in it.

  1. Add Create ZUGFeRD Invoice from the PDF4me PDF connection.
  2. Configure:
    • ZUGFeRD version and Input Format: 2.0-JSON
    • Output Mode: XmlWithPdf
    • Invoice JSON Data: the expression base64(outputs('Compose'))
    • Conformance Level: EN16931
    • Render Invoice on PDF: Yes
    • File Content: a base64() expression over the content from Get file content using path
    • File Name: BaseInvoice.pdf

Create ZUGFeRD Invoice configuration​

PDF4me Create ZUGFeRD Invoice action with ZUGFeRD version and Input Format set to 2.0-JSON, Output Mode XmlWithPdf, the Invoice JSON Data field holding the expression base64 of outputs Compose, Conformance Level EN16931, Render Invoice on PDF Yes, File Content holding a base64 expression over the base PDF and File Name BaseInvoice.pdf, connected to PDF4me PDF

The input format drives which data field appears. Pick 2.0-JSON and the action asks for Invoice JSON Data, not Invoice XML Data.

What each setting decides​

FieldValue hereWhat it controls
ZUGFeRD version and Input Format2.0-JSONWhich ZUGFeRD generation the output follows, and that the data arrives as JSON. This choice is what makes the Invoice JSON Data field appear
Output ModeXmlWithPdfReturn one hybrid file, the PDF with the XML embedded, rather than XML on its own
Invoice JSON Database64(outputs('Compose'))The invoice object, base64 encoded on the way in
Conformance LevelEN16931The profile the invoice claims to meet, and therefore which keys the JSON has to supply
Render Invoice on PDFYesDraw the readable invoice onto the base PDF instead of leaving the page as it was
File Content, File NameThe base PDFThe page the invoice is drawn on, and the name the action reports back

Tip. The action shows Invoice XML Data, Invoice JSON Data or Invoice CSV Data depending on the format half of the dropdown. Fill in only the one that matches your choice. Populating two data fields in the same run is the most common cause of validation failures on this action.


Step 5: Where does the invoice get filed?​

Flow so far: everything through Create ZUGFeRD Invoice.

The invoice number names the file, which keeps the library searchable.

  1. Add SharePoint > Create file.
  2. Configure:
    • Site Address: PDF4me Support - https://ynoox1.sharepoint.com/sites/PDF4meSupport
    • Folder Path: /ZUGFeRD Invoices
    • File Name: ZUGFeRD_ followed by the InvoiceNumber token and .pdf
    • File Content: the Body token from Create ZUGFeRD Invoice

Create file configuration​

SharePoint Create file action with Site Address PDF4me Support, Folder Path /ZUGFeRD Invoices, File Name ZUGFeRD_ followed by the InvoiceNumber token and .pdf, and File Content set to the Body token from the PDF4me action

For RE-2026-0007 this writes ZUGFeRD_RE-2026-0007.pdf.


Step 6: How do you record the result on the request?​

Flow so far: everything through Create file.

The row that asked for the invoice should also say what happened to it.

  1. Add SharePoint > Update item.
  2. Configure:
    • Site Address: PDF4me Support - https://ynoox1.sharepoint.com/sites/PDF4meSupport
    • List Name: ZUGFeRD Requests
    • Id: the ID token from the trigger
    • Title: the Title token from the trigger
    • Status Value (advanced): Generated
    • OutputLink (advanced): the library URL followed by ZUGFeRD_, the InvoiceNumber token and .pdf

Update item configuration​

SharePoint Update item action with Site Address PDF4me Support, List Name ZUGFeRD Requests, Id and Title from the trigger, Status Value set to Generated and OutputLink built from the ZUGFeRD Invoices library URL plus the invoice number

The link is assembled from the library URL and the same name pattern Create file used, with the space in the library name already encoded as %20.


Step 7: How do you send the invoice to the buyer?​

Flow so far: everything through Update item.

An e-invoice that stays in a library has not been issued, so the last action delivers it.

  1. Add Office 365 Outlook > Send an email (V2).
  2. Configure:
    • To: the Email token from the trigger
    • Subject: Your ZUGFeRD e-invoice followed by the InvoiceNumber token
    • Body: a line naming the buyer and the invoice, and saying the file is a PDF/A-3 with embedded EN 16931 XML
  3. Open Advanced parameters and add the attachment:
    • Attachments Name - 1: ZUGFeRD_ plus the InvoiceNumber token plus .pdf
    • Attachments Content - 1: the File Content token from Create ZUGFeRD Invoice
    • Importance: Normal

Send an email (V2) configuration​

Office 365 Outlook Send an email V2 action with To taken from the Email column, a subject ending in the invoice number, a body naming the buyer and describing a PDF/A-3 with embedded EN 16931 XML, an attachment named ZUGFeRD_ plus the invoice number and .pdf whose content is the PDF4me File Content token, and Importance Normal

The attachment takes its bytes from the action that produced them, so SharePoint is never read back.


Run the Flow and Verify​

  1. Save the flow.
  2. Add a row to ZUGFeRD Requests with a buyer, one line item, both dates and an address you can check.
  3. Open the run history. All seven actions should be green, and the whole run should take a few seconds.
  4. Open the ZUGFeRD Invoices library and check the file is there under the invoice number.
  5. Go back to the row: Status should read Generated and OutputLink should open the invoice.
  6. Open the attachment from the email and check both halves: the page should show the right amounts, and the file should carry the XML inside it.

The captured run took about four seconds in total, with the e-invoice itself produced in one of them. The result, ZUGFeRD_RE-2026-0007.pdf, is about 331 KB and renders as a German invoice: seller Muster GmbH in Berlin with VAT ID DE123456789, customer Test Kunde AG in Muenchen, document type 380, one line of Consulting at 19 percent VAT, net 1.005,00 EUR, VAT 190,95 EUR and a total of 1.195,95 EUR.

What did you actually build? An e-invoicing pipeline that starts from your own records. The data stays where finance keeps it, the standard is satisfied by XML you never had to write, the buyer receives a file their software can read automatically and their staff can read on screen, and the request row records that it went out.


Common Variations You Can Add Without Rebuilding​

Invoice more than one line
LineItems is already an array, so this is the easiest change in the flow. Keep the lines in a second list linked by invoice number, fetch them with Get items, and build the array with a Select before the Compose.
Check the output before it goes out
Run the first few invoices through a ZUGFeRD validator and compare what it reports against the Conformance Level you set. It is the fastest way to catch a mapping mistake while the flow is still new.
Keep the XML on its own as well
Some buyers want the structured file separately. Call the action a second time with Output Mode set to XmlOnly and save that result into the library next to the PDF.
Issue for several legal entities
The Seller object already comes from columns, so one list can issue for two companies. Point the base PDF at a letterhead chosen by the same column and the printed page follows.

How this compares to the other ways of producing a ZUGFeRD invoice​

Each row describes the alternative. The last row is this flow.

ComparisonWho runs itWhere the data comes fromWhat you maintain
Invoicing software with ZUGFeRD export vs this flowAnyone, inside that productThe product's own recordsA subscription, and whatever the product supports
E-invoicing platform vs this flowFinance, through a portalUploads or an integrationA per-document contract and a connection to it
Developer library vs this flowA developer, inside an applicationYour database, through codeThe code, the schema updates and the releases
This flowAnyone who can add a list rowA SharePoint list the team keepsOne JSON object and a base PDF

Common questions​

What is a ZUGFeRD invoice?​

A ZUGFeRD invoice is one file that works two ways. It is a normal PDF a person can open and read, and inside it sits an XML attachment holding the same invoice as structured data. The buyer's accounting system reads the XML and books the invoice without anyone typing it in, while the person in accounts payable still sees a page.

The format comes from FeRD, the German forum for electronic invoicing, and its French counterpart is Factur-X, which is the same idea under another name.

What is the ZUGFeRD format?​

Technically it is a PDF/A-3 container with an embedded XML file in the Cross Industry Invoice syntax, and a profile that says how complete the data is. The profile used here is EN16931, which maps to the European semantic standard for e-invoicing: the set of fields that EN 16931 defines as the core of an invoice.

The practical consequence for an automation is that the output is judged on the XML, not on the page. Supply the data the profile asks for and the file passes; make the page prettier and nothing changes.

How to create a ZUGFeRD invoice?​

You need three things: the invoice data in a structured form, a page for the human half, and something that writes the XML into the PDF as a PDF/A-3 attachment.

In this flow the data is a SharePoint row turned into JSON, the page is BaseInvoice.pdf, and Create ZUGFeRD Invoice does the third part with Output Mode XmlWithPdf. If your data already exists as Cross Industry Invoice XML or as a CSV row, switch the input format and feed that instead. The rest of the flow does not change.

What is the difference between ZUGFeRD and XRechnung?​

Both satisfy the same European standard, and the difference is the packaging. ZUGFeRD is hybrid: a readable PDF with the XML inside it, which suits invoices sent to customers who may open them by hand. XRechnung is XML only, and it is the format German public-sector buyers ask for.

If you send to businesses, the hybrid file is usually the safer choice, because it is readable by whoever opens it. If you invoice a public authority, check which format that authority requires before you send.


Troubleshooting​

The action rejects the invoice data

Open the Compose output in the run history and read it as JSON before anything else. An empty column leaves a key empty, and a money value that kept its quotes arrives as text where a number is expected. Both produce an object that looks fine and still fails conversion.

A validator rejects the conformance level

Conformance Level decides which fields the profile requires. Claiming EN16931 while the JSON omits keys that profile expects fails validation, even though the file opens normally. Either supply the missing keys or drop to a lower profile.

The PDF looks blank or still shows the empty template

Render Invoice on PDF is set to No, so the XML was embedded without drawing anything. The file is still a valid e-invoice, but the human half is whatever the base PDF contained.

The buyer says the invoice has no XML

Check the attachment content in the email step and the File Content in Create file. Both must come from Create ZUGFeRD Invoice. Attaching the base PDF instead sends a page that looks right and carries nothing for their software to read.


Next Steps​

The same seven-step pattern (a row arrives, an invoice is described as data, a file is produced, filed, recorded and sent) is what every e-invoicing mandate is really asking for, whichever format your buyers end up needing.