Automation Blog Contact
Back to Blog
Automation Apr 26, 2026 16 min read

E-Invoicing in Microsoft Power Automate: ZUGFeRD, XRechnung, Factur-X and UBL from Dynamics 365

A single HTTP action in Power Automate is all it takes to transform invoice data from Dynamics 365 into a validated, mandate-ready e-invoice. This guide covers format selection, full flow structure, Dynamics 365 field mapping, and format-specific configuration for Germany, France, Belgium, and the Peppol network.

If your business runs on Microsoft 365, Dynamics 365, or Power Platform, e-invoice compliance does not require replacing your existing stack. A single HTTP action in Power Automate is all it takes to transform invoice data from Dynamics 365 Dataverse into a fully validated, mandate-ready e-invoice — ZUGFeRD, XRechnung, Factur-X, or Peppol UBL — and store it in SharePoint or deliver it to your customer automatically.

This post shows you exactly how to build that flow, which format to use for your country, and why adding e-invoice generation as an HTTP action is cleaner than the Word → PDF → email pattern most Microsoft-based businesses still use today.


Why your current Word-to-PDF flow needs to change

Many businesses running Dynamics 365 or Business Central follow a familiar pattern: pull invoice data from Dataverse, generate a Word document from a template, save it to SharePoint, convert it to PDF, email it to the customer.

This worked fine until now. The problem is that since January 2025 in Germany, January 2026 in Belgium, and September 2026 in France, a plain PDF invoice is no longer legally sufficient. Your trading partners and government mandates now require a structured e-invoice — a file that contains machine-readable XML data alongside or instead of the human-readable layout.

Your existing flow produces a PDF. It does not produce a ZUGFeRD file, a Peppol UBL document, or an XRechnung. Those require a different technical process: CII or UBL XML generation, EN 16931 Schematron validation, and — for ZUGFeRD and Factur-X — PDF/A-3b embedding of the XML inside the PDF. None of this is built into Office 365 or Dynamics 365 natively.

The good news: you do not need to rebuild your invoicing flow. You need to replace one step — the Word template generation — with an HTTP action to the InvoiceXML API. Everything else in your flow stays the same.


Which format do you need?

Before building your flow, identify which format applies to your situation. Use this decision table:

Your countryYou invoiceRequired formatEndpoint
🇩🇪 GermanyPrivate companies (B2B)ZUGFeRD/v1/create/zugferd
🇩🇪 GermanyGovernment buyers (B2G)XRechnung/v1/create/xrechnung
🇫🇷 FranceAny business (B2B)Factur-X/v1/create/facturx
🇧🇪 BelgiumAny business (B2B)Peppol UBL/v1/create/ubl
🇳🇱 NetherlandsGovernment buyers (B2G)Peppol UBL/v1/create/ubl
🇳🇴 NorwayAny buyerPeppol UBL (EHF)/v1/create/ubl
🇭🇷 CroatiaAny businessCII or UBL/v1/create/cii
🇵🇹 PortugalAny businessCII (CIUS-PT)/v1/create/cii
🌍 Cross-border EUAny EU businessPeppol UBL/v1/create/ubl

If you operate in multiple countries, use a Switch action in your flow to route to the correct endpoint based on the customer's country field in Dataverse. The API parameters are identical across all endpoints — only the URL changes.


The architecture: before and after

Current flow (Word → PDF):

Dataverse trigger
  → Get invoice record
  → Get line items
  → Populate Word template
  → Convert Word to PDF
  → Save to SharePoint
  → Send email with PDF attachment

New flow (Dataverse → InvoiceXML API):

Dataverse trigger
  → Get invoice record
  → Get line items
  → HTTP POST to InvoiceXML API
  → Save binary response to SharePoint
  → Send email with e-invoice attachment

You remove two steps (Word template population, Word-to-PDF conversion) and replace them with one HTTP action. The output is a validated, mandate-compliant file ready for storage and delivery. No Word template to maintain. No PDF conversion licensing. No compliance logic in your flow.


Setting up the HTTP action

In Power Automate, the InvoiceXML API is called using the built-in HTTP connector (requires a Power Automate Premium licence) or the HTTP with Azure AD connector.

Basic HTTP action configuration

Method:   POST
URI:      https://api.invoicexml.com/v1/create/zugferd
Headers:
  Authorization: Bearer YOUR_API_KEY
  Content-Type:  multipart/form-data
Body:     (see per-format examples below)

Your API key is available in your InvoiceXML dashboard. Store it as a Power Automate Environment Variable or in Azure Key Vault — do not hardcode it in the flow.


Format 1: ZUGFeRD for German B2B

ZUGFeRD is the correct format for invoices between German private-sector companies. The API returns a PDF/A-3b file with embedded CII XML — your customer receives a PDF they can open in any viewer, while their accounting system (DATEV, SAP, Lexoffice, sevDesk) can automatically import the structured data layer.

HTTP action body

In Power Automate, set the body as multipart/form-data. Map each field from your Dynamics 365 invoice record:

{
  "InvoiceNumber":    "@{triggerOutputs()?['body/invoicenumber']}",
  "IssueDate":        "@{formatDateTime(triggerOutputs()?['body/invoicedate'], 'yyyy-MM-dd')}",
  "PaymentDueDate":   "@{formatDateTime(triggerOutputs()?['body/duedate'], 'yyyy-MM-dd')}",
  "SellerName":       "@{triggerOutputs()?['body/accountname']}",
  "SellerTaxId":      "@{triggerOutputs()?['body/taxid']}",
  "SellerStreet":     "@{triggerOutputs()?['body/address1_line1']}",
  "SellerPostcode":   "@{triggerOutputs()?['body/address1_postalcode']}",
  "SellerCity":       "@{triggerOutputs()?['body/address1_city']}",
  "SellerCountry":    "DE",
  "BuyerName":        "@{triggerOutputs()?['body/customeraccountname']}",
  "BuyerTaxId":       "@{triggerOutputs()?['body/customertaxid']}",
  "BuyerStreet":      "@{triggerOutputs()?['body/billingstreet']}",
  "BuyerPostcode":    "@{triggerOutputs()?['body/billingpostalcode']}",
  "BuyerCity":        "@{triggerOutputs()?['body/billingcity']}",
  "BuyerCountry":     "@{triggerOutputs()?['body/billingcountrycode']}",
  "Currency":         "@{triggerOutputs()?['body/transactioncurrencyid']}",
  "TaxBasisTotal":    "@{triggerOutputs()?['body/totalamount_base']}",
  "TaxTotalAmount":   "@{triggerOutputs()?['body/totaltax']}",
  "GrandTotalAmount": "@{triggerOutputs()?['body/totalamountinclusivoftax']}",
  "PaymentMeansCode": "58",
  "IBAN":             "@{triggerOutputs()?['body/iban']}",
  "profile":          "en16931"
}

Line items require an Apply to each loop over your invoice detail records before the HTTP action, building a lines array. Alternatively, pass the line items as indexed parameters:

Lines[0][description]    →  @{items('Apply_to_each')?['productdescription']}
Lines[0][quantity]       →  @{items('Apply_to_each')?['quantity']}
Lines[0][unitPrice]      →  @{items('Apply_to_each')?['priceperunit']}
Lines[0][lineTotal]      →  @{items('Apply_to_each')?['extendedamount']}
Lines[0][unitCode]       →  HUR
Lines[0][taxPercentage]  →  19
Lines[0][taxCategoryCode]→  S

Handling the response

The API returns the ZUGFeRD PDF as a binary file (application/pdf). In Power Automate:

Create file (SharePoint)
  Site:         Your SharePoint site
  Folder:       /Invoices/ZUGFeRD/
  File name:    @{triggerOutputs()?['body/invoicenumber']}.pdf
  File content: @{body('HTTP')}

Then send it by email:

Send an email (Outlook)
  To:              @{triggerOutputs()?['body/emailaddress']}
  Subject:         Invoice @{triggerOutputs()?['body/invoicenumber']}
  Attachments:     @{body('HTTP')}
  Attachment name: @{triggerOutputs()?['body/invoicenumber']}.pdf

The file stored in SharePoint is GoBD-compliant for German archiving purposes — PDF/A-3b format satisfies the immutability and long-term readability requirements of German digital bookkeeping law.


Format 2: XRechnung for German B2G

XRechnung is mandatory for invoices to German federal, state, and municipal buyers. Unlike ZUGFeRD it is pure XML — no PDF wrapper. It is submitted via the ZRE or OZG-RE portal, or transmitted via the Peppol network.

The critical difference from ZUGFeRD: you must supply the Leitweg-ID. This is a routing identifier provided by your government buyer at contract time. It cannot be extracted from any invoice field — it must be stored in your system against the customer record.

Add a custom field to your Dynamics 365 account record for the Leitweg-ID (e.g. cr_leitwegid). Make it mandatory for public sector customers.

HTTP action configuration

URI: https://api.invoicexml.com/v1/create/xrechnung

Add one additional parameter compared to the ZUGFeRD call:

"BuyerReference": "@{triggerOutputs()?['body/cr_leitwegid']}"

If this field is empty, the API returns a 4002 error immediately. Add a condition before the HTTP action:

Condition: cr_leitwegid is not empty
  → If yes: proceed to HTTP action
  → If no:  send notification email to invoice team
            "XRechnung generation failed: Leitweg-ID missing for [customer name]"

The API returns XRechnung XML (application/xml). Store it in SharePoint and submit to your Peppol access point or upload manually to the ZRE portal.

Choosing CII or UBL syntax

XRechnung supports both. The default is CII. If your submission portal requires UBL syntax, add:

"syntax": "ubl"

Both are legally equivalent and accepted by all German public sector portals.


Format 3: Factur-X for French B2B

Factur-X is the correct format for French B2B invoicing, mandatory from September 2026. Like ZUGFeRD, it is a hybrid format — a PDF/A-3b with embedded CII XML — so your French customers receive a readable PDF while their accounting system imports the structured data.

URI: https://api.invoicexml.com/v1/create/facturx

The parameters are identical to ZUGFeRD. Change SellerCountry and BuyerCountry to FR and update the tax rate to 20% (French standard VAT rate):

"Lines[0][taxPercentage]": "20"

French invoices also commonly include the SIREN/SIRET number in the seller tax ID field — format it as FR + 2-digit key + 9-digit SIREN:

"SellerTaxId": "@{triggerOutputs()?['body/fr_taxid']}"

The response is a PDF/A-3b file ready for submission to a certified Plateforme Agréée (PDP) or the public PPF portal from September 2026.


Format 4: Peppol UBL for Belgium, Netherlands, and Peppol markets

Peppol UBL is mandatory for Belgian B2B invoicing since January 2026, and the standard for the Netherlands, Norway, Denmark, and cross-border Peppol transactions globally. Unlike ZUGFeRD and Factur-X, the output is pure UBL XML — it must be transmitted via a Peppol access point, not emailed directly.

URI: https://api.invoicexml.com/v1/create/ubl

Add the profile parameter to set the correct CIUS for the destination country:

"profile": "peppol-bis-3"    ← Belgium, Denmark, Sweden, cross-border
"profile": "nlcius"           ← Netherlands public sector
"profile": "ehf"              ← Norway

For Belgian invoices, you also need the buyer's enterprise number as an endpoint identifier. Add a custom field to your Dynamics account record for the Belgian enterprise number (BE + 10 digits):

"BuyerEndpointId":     "@{triggerOutputs()?['body/cr_enterprisenumber']}",
"BuyerEndpointScheme": "0208"

The 0208 scheme code identifies Belgian Crossroads Bank for Enterprises (KBO/BCE) numbers in the Peppol network.

After generating the UBL XML, your flow should pass it to your Peppol access point provider's API for network delivery, rather than emailing it directly. The SharePoint save step remains the same for archiving.


Validating before storing and sending

For high-stakes or high-volume flows, add a validation step before storing and sending. This catches any field mapping errors — missing VAT numbers, arithmetic inconsistencies, malformed IBANs — before a non-compliant invoice reaches your customer.

Add an HTTP action immediately after the create call:

Method: POST
URI:    https://api.invoicexml.com/v1/validate/zugferd
        (or /validate/xrechnung, /validate/ubl)
Body:   file: @{body('Create_Invoice_HTTP')}

Then add a condition:

Condition: @{body('Validate_HTTP')?['valid']} is equal to true
  → If yes: save to SharePoint, send to customer
  → If no:  send internal alert with error details
            "@{body('Validate_HTTP')?['errors']?['friendly']}"

The errors.friendly array contains plain-language error descriptions you can include directly in a Teams notification or email to your finance team — no technical knowledge required to understand what needs fixing.


Complete flow structure

Here is the complete Power Automate flow for German ZUGFeRD invoicing:

 1. Trigger: When a Dataverse record is updated
    (invoice status changes to "Ready to Send")

 2. Get invoice record
    (fetch all fields from the invoice entity)

 3. Get invoice line items
    (list rows from invoice detail entity, filtered by invoice ID)

 4. Build lines array
    (Apply to each → compose line item parameters)

 5. HTTP: Create ZUGFeRD
    POST https://api.invoicexml.com/v1/create/zugferd
    [invoice parameters + line items]

 6. Condition: HTTP status code is 200
    → If no: Send Teams notification "Invoice generation failed"
             Include: @{body('HTTP')?['detail']}
             Terminate flow

 7. HTTP: Validate ZUGFeRD
    POST https://api.invoicexml.com/v1/validate/zugferd
    file: @{body('Create_ZUGFeRD_HTTP')}

 8. Condition: valid is true
    → If no: Send Teams notification "Invoice validation failed"
             Include: @{body('Validate_HTTP')?['errors']?['friendly']}
             Terminate flow

 9. Create file (SharePoint)
    /Invoices/ZUGFeRD/@{invoicenumber}.pdf
    Content: @{body('Create_ZUGFeRD_HTTP')}

10. Update Dataverse record
    invoice status → "Sent"
    invoice file path → SharePoint URL

11. Send email (Outlook)
    To: customer email
    Attachment: @{body('Create_ZUGFeRD_HTTP')}

Dynamics 365 field mapping reference

Standard Dynamics 365 invoice entity fields and their InvoiceXML parameter equivalents:

Dynamics 365 fieldDataverse logical nameInvoiceXML parameter
Invoice NumberinvoicenumberInvoiceNumber
Invoice DatecreatedonIssueDate
Due DateduedatePaymentDueDate
Account Namecustomerid_account.nameBuyerName
Billing Streetbillto_line1BuyerStreet
Billing Postal Codebillto_postalcodeBuyerPostcode
Billing Citybillto_cityBuyerCity
Total TaxtotaltaxTaxTotalAmount
Total AmounttotalamountTaxBasisTotal
Total incl. TaxtotalamountlessfreightGrandTotalAmount
CurrencytransactioncurrencyidCurrency
Product Descriptioninvoicedetail.productdescriptionLines[n][description]
Quantityinvoicedetail.quantityLines[n][quantity]
Price Per Unitinvoicedetail.priceperunitLines[n][unitPrice]
Extended Amountinvoicedetail.extendedamountLines[n][lineTotal]

Your seller details (name, VAT number, address, IBAN) are typically static — store them as Power Automate Environment Variables rather than fetching from Dataverse on every run.


Why this beats building it into your Dynamics customisation

The alternative to using InvoiceXML is implementing ZUGFeRD or UBL generation inside Dynamics 365 itself — either as a custom plugin, a PCF control, or a .NET Azure Function that your cloud flow calls. This is technically achievable. Here is what it actually involves:

For ZUGFeRD: CII XML generation, Saxon-HE Schematron validation (requires IKVM cross-compilation for .NET), PDF/A-3b container creation with correct ICC colour profiles, XMP metadata, and AFRelationship declarations. The PDF/A-3b layer alone has four distinct failure modes that are invisible in a standard PDF viewer.

For Peppol UBL: UBL 2.1 XML generation, EN 16931 Schematron, Peppol BIS 3.0 Schematron overlay (updates quarterly from OpenPeppol), CustomizationID profile routing across multiple national CIUS variants.

The maintenance commitment: Every time FeRD publishes a new ZUGFeRD version, every time KoSIT releases a new XRechnung CIUS, every time OpenPeppol updates Peppol BIS, your custom implementation needs to be updated, tested, and redeployed. These updates happen multiple times per year across the formats combined.

InvoiceXML maintains all of this as a service. When ZUGFeRD 2.4 shipped in 2025 with mandatory service period dates and deprecated NIL elements, our validation rules updated before the version's effective date. Your flow did not change. Your customers did not see a rejection.

Additionally: invoices contain sensitive financial data — VAT numbers, IBANs, payment amounts, business relationships. Every invoice that passes through a self-hosted processing function is a GDPR consideration. InvoiceXML is stateless by design — every document is processed in memory and purged immediately on response delivery. Nothing is written to disk, nothing is retained. GDPR and HIPAA compliant by architecture, not configuration.


Microsoft Power Apps and other Power Platform tools

The same HTTP action pattern works across the Power Platform:

Power Apps: Call the InvoiceXML API from a Power Apps custom connector. Add a button to your Dynamics-connected app that generates and downloads a ZUGFeRD invoice on demand, without leaving the application.

Logic Apps: The Azure Logic Apps equivalent of Power Automate. The HTTP action configuration is identical — useful when your invoicing flow runs as part of a broader Azure-hosted integration.

Azure Functions: For high-volume or programmatic scenarios, call the InvoiceXML API from an Azure Function triggered by a Dataverse change event. The Function handles batching, error retry logic, and Peppol access point submission in a single serverless workflow.

SharePoint + Power Automate: Trigger invoice generation from a SharePoint list item status change. Store the resulting e-invoice back to the same library with a structured folder hierarchy — /Invoices/{Year}/{Month}/{InvoiceNumber}.pdf.


Get started

Create your free InvoiceXML account → — includes 100 free credits. No credit card required for the trial.

If you are building a Power Automate flow and want help mapping your specific Dynamics 365 fields to the API parameters, contact us directly — we are happy to review your setup and suggest the correct field mapping for your invoice entity structure.

Related resources:


InvoiceXML is the e-invoice compliance layer for Microsoft Power Platform, Dynamics 365, and any cloud-based invoicing workflow. It handles ZUGFeRD, Factur-X, XRechnung, UBL, and CII via a single REST API — with stateless processing, automatic standard updates, and no compliance infrastructure to maintain inside your Dynamics customisation.


Ready to automate your invoices?

Start your 30-day free trial. No credit card required.

Get Started