§HOW-TO GUIDE · C# · .NET 8 & 10

Create an XRechnung invoice in C#

Convert the visible invoice to PDF/A-3, then call AddZugferdInvoice with ZugferdProfile.XRechnung: the XRechnung XML is embedded as xrechnung.xml and the document metadata marks it as an XRechnung invoice.

Compiled and run: Linux ARM64 · Linux x64 · macOS ARM64 · Windows x64

Validated by veraPDF: PDF/A-3b · PDF/A-3a

Video walkthrough · 3:42 · Watch on YouTube · Short · 1:08
§STEP BY STEP

Build it step by step.

  1. 1Install the NuGet packages

    SelectPdf.Universal is the library. The native package carries the Chromium engine for one platform: add the one for the platform you run on.

    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.win-x64
    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.linux-x64
    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.linux-arm64
    terminal
    dotnet add package SelectPdf.Universal
    dotnet add package SelectPdf.Universal.Native.osx-arm64
  2. 2Create a console app

    Any .NET 8 or .NET 10 project works. To follow along, create a console app, run the install commands above inside its folder, then replace Program.cs with the code below.

    terminal
    dotnet new console -n PdfSample
    cd PdfSample
  3. 3Start from the XRechnung XML

    Program.cs · lines 4–42
    // the invoice as XRechnung CII XML (shortened); in your application, your invoicing system produces it
    string xml = """
        <?xml version="1.0" encoding="UTF-8"?>
        <rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
          xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
          xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
          <rsm:ExchangedDocumentContext><ram:GuidelineSpecifiedDocumentContextParameter>
            <ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</ram:ID>
          </ram:GuidelineSpecifiedDocumentContextParameter></rsm:ExchangedDocumentContext>
          <rsm:ExchangedDocument>
            <ram:ID>RE-2026-0815</ram:ID><ram:TypeCode>380</ram:TypeCode>
            <ram:IssueDateTime><udt:DateTimeString format="102">20261013</udt:DateTimeString></ram:IssueDateTime>
          </rsm:ExchangedDocument>
          <rsm:SupplyChainTradeTransaction>
            <ram:IncludedSupplyChainTradeLineItem>
              <ram:AssociatedDocumentLineDocument><ram:LineID>1</ram:LineID></ram:AssociatedDocumentLineDocument>
              <ram:SpecifiedTradeProduct><ram:Name>Website maintenance, September 2026</ram:Name></ram:SpecifiedTradeProduct>
              <ram:SpecifiedLineTradeSettlement><ram:SpecifiedTradeSettlementLineMonetarySummation>
                <ram:LineTotalAmount>1000.00</ram:LineTotalAmount>
              </ram:SpecifiedTradeSettlementLineMonetarySummation></ram:SpecifiedLineTradeSettlement>
            </ram:IncludedSupplyChainTradeLineItem>
            <ram:ApplicableHeaderTradeAgreement>
              <ram:BuyerReference>04011000-12345-34</ram:BuyerReference>
              <ram:SellerTradeParty><ram:Name>Contoso GmbH</ram:Name></ram:SellerTradeParty>
              <ram:BuyerTradeParty><ram:Name>Stadt Musterstadt</ram:Name></ram:BuyerTradeParty>
            </ram:ApplicableHeaderTradeAgreement>
            <ram:ApplicableHeaderTradeDelivery/>
            <ram:ApplicableHeaderTradeSettlement>
              <ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
              <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
                <ram:TaxBasisTotalAmount>1000.00</ram:TaxBasisTotalAmount>
                <ram:TaxTotalAmount currencyID="EUR">190.00</ram:TaxTotalAmount>
                <ram:GrandTotalAmount>1190.00</ram:GrandTotalAmount>
                <ram:DuePayableAmount>1190.00</ram:DuePayableAmount>
              </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
            </ram:ApplicableHeaderTradeSettlement>
          </rsm:SupplyChainTradeTransaction>
        </rsm:CrossIndustryInvoice>
        """;

    The invoice data as an XRechnung document in the UN/CEFACT Cross Industry Invoice syntax, shortened for this guide. The guideline ID in ExchangedDocumentContext names XRechnung, and BuyerReference carries the buyer's reference. In your application, your invoicing system produces this XML; SelectPdf embeds it as it is and does not generate or validate it.

  4. 4Write the visible invoice

    Program.cs · lines 44–49
    // the visible invoice, with the same content as the XML
    string html = """
        <h1>Invoice RE-2026-0815</h1>
        <p>13 October 2026 · Contoso GmbH to Stadt Musterstadt · Buyer reference 04011000-12345-34</p>
        <p>Website maintenance, September 2026: 1,000.00 EUR<br>VAT 19%: 190.00 EUR<br><b>Total: 1,190.00 EUR</b></p>
        """;

    The page a person reads. It shows the same invoice as the XML: number, date, parties, buyer reference, line and totals. The Alternative relationship used below states exactly that.

  5. 5Convert to PDF/A-3

    Program.cs · lines 51–55
    // the carrier: a PDF/A-3 document
    HtmlToPdf converter = new HtmlToPdf();
    converter.Options.PdfStandard = PdfStandard.PdfA3B;
    converter.Options.PdfDocumentInformation.Title = "Invoice RE-2026-0815";
    PdfDocument doc = converter.ConvertHtmlString(html);

    Options.PdfStandard = PdfStandard.PdfA3B makes the converted invoice a PDF/A-3 document, the carrier format hybrid invoices require. PdfDocumentInformation.Title records the invoice number in the document metadata.

  6. 6Embed the XML with the XRechnung profile

    Program.cs · lines 57–61
    // embed the XML as xrechnung.xml and mark the document as an XRechnung invoice
    using (MemoryStream invoiceXml = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
    {
        doc.AddZugferdInvoice(invoiceXml, ZugferdProfile.XRechnung, PdfAttachmentRelationship.Alternative);
    }

    AddZugferdInvoice reads the stream immediately, embeds it as an associated file named xrechnung.xml and writes the invoice metadata, with XRECHNUNG as the conformance level. PdfAttachmentRelationship.Alternative says the XML and the page carry the same content.

  7. 7Save

    Program.cs · lines 63–64
    doc.Save("invoice-xrechnung.pdf");
    doc.Close();

    Save writes invoice-xrechnung.pdf and Close releases the document. The file passes veraPDF as PDF/A-3b and lists xrechnung.xml as its embedded file.

  8. 8Run it

    terminal
    dotnet run

    The program writes invoice-xrechnung.pdf to the working folder. Without a license key, every page carries a trial watermark; set GlobalProperties.LicenseKey before the first conversion to remove it.

§COMPLETE PROGRAM

The complete Program.cs.

This is the exact file our samples pipeline compiled and ran on Linux ARM64 and Linux x64 and macOS ARM64 and Windows x64 to produce the output on this page.

Program.cs
using System.Text;
using SelectPdf.Universal;

// the invoice as XRechnung CII XML (shortened); in your application, your invoicing system produces it
string xml = """
    <?xml version="1.0" encoding="UTF-8"?>
    <rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
      xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
      xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
      <rsm:ExchangedDocumentContext><ram:GuidelineSpecifiedDocumentContextParameter>
        <ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</ram:ID>
      </ram:GuidelineSpecifiedDocumentContextParameter></rsm:ExchangedDocumentContext>
      <rsm:ExchangedDocument>
        <ram:ID>RE-2026-0815</ram:ID><ram:TypeCode>380</ram:TypeCode>
        <ram:IssueDateTime><udt:DateTimeString format="102">20261013</udt:DateTimeString></ram:IssueDateTime>
      </rsm:ExchangedDocument>
      <rsm:SupplyChainTradeTransaction>
        <ram:IncludedSupplyChainTradeLineItem>
          <ram:AssociatedDocumentLineDocument><ram:LineID>1</ram:LineID></ram:AssociatedDocumentLineDocument>
          <ram:SpecifiedTradeProduct><ram:Name>Website maintenance, September 2026</ram:Name></ram:SpecifiedTradeProduct>
          <ram:SpecifiedLineTradeSettlement><ram:SpecifiedTradeSettlementLineMonetarySummation>
            <ram:LineTotalAmount>1000.00</ram:LineTotalAmount>
          </ram:SpecifiedTradeSettlementLineMonetarySummation></ram:SpecifiedLineTradeSettlement>
        </ram:IncludedSupplyChainTradeLineItem>
        <ram:ApplicableHeaderTradeAgreement>
          <ram:BuyerReference>04011000-12345-34</ram:BuyerReference>
          <ram:SellerTradeParty><ram:Name>Contoso GmbH</ram:Name></ram:SellerTradeParty>
          <ram:BuyerTradeParty><ram:Name>Stadt Musterstadt</ram:Name></ram:BuyerTradeParty>
        </ram:ApplicableHeaderTradeAgreement>
        <ram:ApplicableHeaderTradeDelivery/>
        <ram:ApplicableHeaderTradeSettlement>
          <ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
          <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
            <ram:TaxBasisTotalAmount>1000.00</ram:TaxBasisTotalAmount>
            <ram:TaxTotalAmount currencyID="EUR">190.00</ram:TaxTotalAmount>
            <ram:GrandTotalAmount>1190.00</ram:GrandTotalAmount>
            <ram:DuePayableAmount>1190.00</ram:DuePayableAmount>
          </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
        </ram:ApplicableHeaderTradeSettlement>
      </rsm:SupplyChainTradeTransaction>
    </rsm:CrossIndustryInvoice>
    """;

// the visible invoice, with the same content as the XML
string html = """
    <h1>Invoice RE-2026-0815</h1>
    <p>13 October 2026 · Contoso GmbH to Stadt Musterstadt · Buyer reference 04011000-12345-34</p>
    <p>Website maintenance, September 2026: 1,000.00 EUR<br>VAT 19%: 190.00 EUR<br><b>Total: 1,190.00 EUR</b></p>
    """;

// the carrier: a PDF/A-3 document
HtmlToPdf converter = new HtmlToPdf();
converter.Options.PdfStandard = PdfStandard.PdfA3B;
converter.Options.PdfDocumentInformation.Title = "Invoice RE-2026-0815";
PdfDocument doc = converter.ConvertHtmlString(html);

// embed the XML as xrechnung.xml and mark the document as an XRechnung invoice
using (MemoryStream invoiceXml = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
{
    doc.AddZugferdInvoice(invoiceXml, ZugferdProfile.XRechnung, PdfAttachmentRelationship.Alternative);
}

doc.Save("invoice-xrechnung.pdf");
doc.Close();

Open the project on GitHub → this program and the variations below, ready to dotnet run

§OUTPUT

The PDF it produces.

First page of invoice-xrechnung.pdf, the PDF this sample produces
invoice-xrechnung.pdf, page 1

Download invoice-xrechnung.pdf ↓ the file this sample produced, unedited

§VARIATIONS

Variations you will need next.

Each variation is a separate file in the same project, compiled and run with the program above.

An accessible XRechnung invoice (PDF/A-3A with PDF/UA-1)

PdfStandard.PdfA3A with AccessibilityStandard = PdfUAStandard.UA1 makes the carrier a tagged, accessible document, which the hybrid invoice standards recommend. PDF/UA needs a title and a language, so Options.Title and Options.Language are set. The XML is embedded the same way.

AccessibleXRechnung.cs
using System.Text;
using SelectPdf.Universal;

static class AccessibleXRechnung
{
    public static void Run()
    {
        string xml = """
            <?xml version="1.0" encoding="UTF-8"?>
            <rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
              xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
              xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
              <rsm:ExchangedDocumentContext><ram:GuidelineSpecifiedDocumentContextParameter>
                <ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</ram:ID>
              </ram:GuidelineSpecifiedDocumentContextParameter></rsm:ExchangedDocumentContext>
              <rsm:ExchangedDocument>
                <ram:ID>RE-2026-0816</ram:ID><ram:TypeCode>380</ram:TypeCode>
                <ram:IssueDateTime><udt:DateTimeString format="102">20261013</udt:DateTimeString></ram:IssueDateTime>
              </rsm:ExchangedDocument>
              <rsm:SupplyChainTradeTransaction>
                <ram:IncludedSupplyChainTradeLineItem>
                  <ram:AssociatedDocumentLineDocument><ram:LineID>1</ram:LineID></ram:AssociatedDocumentLineDocument>
                  <ram:SpecifiedTradeProduct><ram:Name>Hosting, October 2026</ram:Name></ram:SpecifiedTradeProduct>
                  <ram:SpecifiedLineTradeSettlement><ram:SpecifiedTradeSettlementLineMonetarySummation>
                    <ram:LineTotalAmount>500.00</ram:LineTotalAmount>
                  </ram:SpecifiedTradeSettlementLineMonetarySummation></ram:SpecifiedLineTradeSettlement>
                </ram:IncludedSupplyChainTradeLineItem>
                <ram:ApplicableHeaderTradeAgreement>
                  <ram:BuyerReference>04011000-12345-34</ram:BuyerReference>
                  <ram:SellerTradeParty><ram:Name>Contoso GmbH</ram:Name></ram:SellerTradeParty>
                  <ram:BuyerTradeParty><ram:Name>Stadt Musterstadt</ram:Name></ram:BuyerTradeParty>
                </ram:ApplicableHeaderTradeAgreement>
                <ram:ApplicableHeaderTradeDelivery/>
                <ram:ApplicableHeaderTradeSettlement>
                  <ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
                  <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
                    <ram:TaxBasisTotalAmount>500.00</ram:TaxBasisTotalAmount>
                    <ram:TaxTotalAmount currencyID="EUR">95.00</ram:TaxTotalAmount>
                    <ram:GrandTotalAmount>595.00</ram:GrandTotalAmount>
                    <ram:DuePayableAmount>595.00</ram:DuePayableAmount>
                  </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
                </ram:ApplicableHeaderTradeSettlement>
              </rsm:SupplyChainTradeTransaction>
            </rsm:CrossIndustryInvoice>
            """;

        HtmlToPdf converter = new HtmlToPdf();

        // PDF/A-3A with PDF/UA-1: an archival carrier whose visible invoice is also accessible
        converter.Options.PdfStandard = PdfStandard.PdfA3A;
        converter.Options.AccessibilityStandard = PdfUAStandard.UA1;
        converter.Options.Title = "Invoice RE-2026-0816";
        converter.Options.Language = "en-US";

        PdfDocument doc = converter.ConvertHtmlString("""
            <h1>Invoice RE-2026-0816</h1>
            <p>13 October 2026. Contoso GmbH to Stadt Musterstadt. Buyer reference 04011000-12345-34.</p>
            <table>
              <tr><th scope="col">Item</th><th scope="col">Amount</th></tr>
              <tr><td>Hosting, October 2026</td><td>500.00 EUR</td></tr>
              <tr><td>VAT 19%</td><td>95.00 EUR</td></tr>
              <tr><td>Total</td><td>595.00 EUR</td></tr>
            </table>
            """);

        using (MemoryStream invoiceXml = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
        {
            doc.AddZugferdInvoice(invoiceXml, ZugferdProfile.XRechnung, PdfAttachmentRelationship.Alternative);
        }

        doc.Save("invoice-xrechnung-pdfa3a.pdf");
        doc.Close();
    }
}

Turn an existing PDF invoice into an XRechnung invoice

An invoice that already exists as a PDF is loaded with new PdfDocument(path) and converted with ConvertToPdfA(PdfStandard.PdfA3B); a document that is already PDF/A-3 needs no conversion. This overload of AddZugferdInvoice takes the path of the XML file, reads it immediately and embeds it as xrechnung.xml.

ExistingPdfToXRechnung.cs
using SelectPdf.Universal;

static class ExistingPdfToXRechnung
{
    public static void Run()
    {
        // stands in for an invoice your system already produced as a PDF, and for its XML
        HtmlToPdf converter = new HtmlToPdf();
        PdfDocument plain = converter.ConvertHtmlString(
            "<h1>Invoice RE-2026-0817</h1><p>13 October 2026. Contoso GmbH to Stadt Musterstadt. " +
            "Buyer reference 04011000-12345-34.</p><p>Software licence: 2,000.00 EUR<br>" +
            "VAT 19%: 380.00 EUR<br><b>Total: 2,380.00 EUR</b></p>");
        plain.Save("invoice-0817.pdf");
        plain.Close();
        File.WriteAllText("invoice-0817.xml", """
            <?xml version="1.0" encoding="UTF-8"?>
            <rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
              xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
              xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
              <rsm:ExchangedDocumentContext><ram:GuidelineSpecifiedDocumentContextParameter>
                <ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</ram:ID>
              </ram:GuidelineSpecifiedDocumentContextParameter></rsm:ExchangedDocumentContext>
              <rsm:ExchangedDocument>
                <ram:ID>RE-2026-0817</ram:ID><ram:TypeCode>380</ram:TypeCode>
                <ram:IssueDateTime><udt:DateTimeString format="102">20261013</udt:DateTimeString></ram:IssueDateTime>
              </rsm:ExchangedDocument>
              <rsm:SupplyChainTradeTransaction>
                <ram:IncludedSupplyChainTradeLineItem>
                  <ram:AssociatedDocumentLineDocument><ram:LineID>1</ram:LineID></ram:AssociatedDocumentLineDocument>
                  <ram:SpecifiedTradeProduct><ram:Name>Software licence</ram:Name></ram:SpecifiedTradeProduct>
                  <ram:SpecifiedLineTradeSettlement><ram:SpecifiedTradeSettlementLineMonetarySummation>
                    <ram:LineTotalAmount>2000.00</ram:LineTotalAmount>
                  </ram:SpecifiedTradeSettlementLineMonetarySummation></ram:SpecifiedLineTradeSettlement>
                </ram:IncludedSupplyChainTradeLineItem>
                <ram:ApplicableHeaderTradeAgreement>
                  <ram:BuyerReference>04011000-12345-34</ram:BuyerReference>
                  <ram:SellerTradeParty><ram:Name>Contoso GmbH</ram:Name></ram:SellerTradeParty>
                  <ram:BuyerTradeParty><ram:Name>Stadt Musterstadt</ram:Name></ram:BuyerTradeParty>
                </ram:ApplicableHeaderTradeAgreement>
                <ram:ApplicableHeaderTradeDelivery/>
                <ram:ApplicableHeaderTradeSettlement>
                  <ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
                  <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
                    <ram:TaxBasisTotalAmount>2000.00</ram:TaxBasisTotalAmount>
                    <ram:TaxTotalAmount currencyID="EUR">380.00</ram:TaxTotalAmount>
                    <ram:GrandTotalAmount>2380.00</ram:GrandTotalAmount>
                    <ram:DuePayableAmount>2380.00</ram:DuePayableAmount>
                  </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
                </ram:ApplicableHeaderTradeSettlement>
              </rsm:SupplyChainTradeTransaction>
            </rsm:CrossIndustryInvoice>
            """);

        // load the existing invoice and make it an archival PDF/A-3 document
        PdfDocument doc = new PdfDocument("invoice-0817.pdf");
        doc.ConvertToPdfA(PdfStandard.PdfA3B);

        // embed the XML from its file; it is read immediately and embedded as xrechnung.xml
        doc.AddZugferdInvoice("invoice-0817.xml", ZugferdProfile.XRechnung,
            PdfAttachmentRelationship.Alternative);

        doc.Save("invoice-0817-xrechnung.pdf");
        doc.Close();
    }
}

Add metadata of your own next to the invoice

A PDF/A document may only carry metadata from the schemas the standard predefines, unless the schema is described inside the document. AddXmpExtensionSchema declares a schema of your own and writes its values, here an order number and a department, so the document stays conformant.

XRechnungWithCustomMetadata.cs
using System.Text;
using SelectPdf.Universal;

static class XRechnungWithCustomMetadata
{
    public static void Run()
    {
        string xml = """
            <?xml version="1.0" encoding="UTF-8"?>
            <rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
              xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
              xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
              <rsm:ExchangedDocumentContext><ram:GuidelineSpecifiedDocumentContextParameter>
                <ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</ram:ID>
              </ram:GuidelineSpecifiedDocumentContextParameter></rsm:ExchangedDocumentContext>
              <rsm:ExchangedDocument>
                <ram:ID>RE-2026-0818</ram:ID><ram:TypeCode>380</ram:TypeCode>
                <ram:IssueDateTime><udt:DateTimeString format="102">20261013</udt:DateTimeString></ram:IssueDateTime>
              </rsm:ExchangedDocument>
              <rsm:SupplyChainTradeTransaction>
                <ram:IncludedSupplyChainTradeLineItem>
                  <ram:AssociatedDocumentLineDocument><ram:LineID>1</ram:LineID></ram:AssociatedDocumentLineDocument>
                  <ram:SpecifiedTradeProduct><ram:Name>Support contract, Q4 2026</ram:Name></ram:SpecifiedTradeProduct>
                  <ram:SpecifiedLineTradeSettlement><ram:SpecifiedTradeSettlementLineMonetarySummation>
                    <ram:LineTotalAmount>750.00</ram:LineTotalAmount>
                  </ram:SpecifiedTradeSettlementLineMonetarySummation></ram:SpecifiedLineTradeSettlement>
                </ram:IncludedSupplyChainTradeLineItem>
                <ram:ApplicableHeaderTradeAgreement>
                  <ram:BuyerReference>04011000-12345-34</ram:BuyerReference>
                  <ram:SellerTradeParty><ram:Name>Contoso GmbH</ram:Name></ram:SellerTradeParty>
                  <ram:BuyerTradeParty><ram:Name>Stadt Musterstadt</ram:Name></ram:BuyerTradeParty>
                </ram:ApplicableHeaderTradeAgreement>
                <ram:ApplicableHeaderTradeDelivery/>
                <ram:ApplicableHeaderTradeSettlement>
                  <ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
                  <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
                    <ram:TaxBasisTotalAmount>750.00</ram:TaxBasisTotalAmount>
                    <ram:TaxTotalAmount currencyID="EUR">142.50</ram:TaxTotalAmount>
                    <ram:GrandTotalAmount>892.50</ram:GrandTotalAmount>
                    <ram:DuePayableAmount>892.50</ram:DuePayableAmount>
                  </ram:SpecifiedTradeSettlementHeaderMonetarySummation>
                </ram:ApplicableHeaderTradeSettlement>
              </rsm:SupplyChainTradeTransaction>
            </rsm:CrossIndustryInvoice>
            """;

        HtmlToPdf converter = new HtmlToPdf();
        converter.Options.PdfStandard = PdfStandard.PdfA3B;
        PdfDocument doc = converter.ConvertHtmlString(
            "<h1>Invoice RE-2026-0818</h1><p>13 October 2026. Contoso GmbH to Stadt Musterstadt. " +
            "Buyer reference 04011000-12345-34.</p><p>Support contract, Q4 2026: 750.00 EUR<br>" +
            "VAT 19%: 142.50 EUR<br><b>Total: 892.50 EUR</b></p>");

        using (MemoryStream invoiceXml = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
        {
            doc.AddZugferdInvoice(invoiceXml, ZugferdProfile.XRechnung, PdfAttachmentRelationship.Alternative);
        }

        // metadata of your own, declared as an XMP extension schema so the document stays PDF/A
        Dictionary<string, string> properties = new Dictionary<string, string>();
        properties["OrderNumber"] = "PO-4711";
        properties["Department"] = "Accounts receivable";
        doc.AddXmpExtensionSchema("contoso", "urn:contoso:pdfa:invoice:1p0#",
            "Contoso invoice schema", properties);

        doc.Save("invoice-0818-xrechnung.pdf");
        doc.Close();
    }
}
§TROUBLESHOOTING

Common problems and fixes.

Could not add the electronic invoice: PDF/A-3 required

AddZugferdInvoice throws a PdfDocumentException on a document that is not PDF/A-3. Set Options.PdfStandard to PdfA3B, PdfA3U or PdfA3A before converting, create the document with new PdfDocument(PdfStandard.PdfA3B), or convert a loaded document with ConvertToPdfA(PdfStandard.PdfA3B).

The embedded file is named factur-x.xml

The file name follows the profile. Pass ZugferdProfile.XRechnung to get xrechnung.xml; the other profiles embed factur-x.xml, or zugferd-invoice.xml with the ZUGFeRD 2.0 schema.

The recipient rejects the invoice data

SelectPdf does not generate or validate the XML: it embeds what you pass. Check the XML against the XRechnung rules with your invoicing tools, and make sure the visible page shows the same content, as the Alternative relationship states.

Which relationship to use

For XRechnung use PdfAttachmentRelationship.Alternative: the XML and the visible page carry exactly the same invoice content, which is mandatory in Germany for this profile. Data is for a page that carries more than the XML, and Source for a page generated from the XML.

§GOOD TO KNOW

Good to know.

  • ZugferdProfile.XRechnung is the German public-sector reference profile; with it the embedded XML file is named xrechnung.xml instead of factur-x.xml. Source
  • The document must be PDF/A-3 (PdfA3B, PdfA3U or PdfA3A), which the hybrid invoice standards require as the carrier format. Source
  • The Alternative relationship, meaning the XML and the visible page carry exactly the same invoice content, is mandatory in Germany for the XRECHNUNG profile. Source
  • SelectPdf embeds and identifies the invoice XML you supply; producing it, and making sure it matches the visible invoice, is your application's job. Source
  • Electronic invoices are available only in the full commercial SelectPdf library, not in the free Community Edition. Source
?FAQ

Questions, answered.

Short answers about this guide. The SelectPdf.Universal documentation covers every class and option.

Convert the invoice page with converter.Options.PdfStandard = PdfStandard.PdfA3B, then call doc.AddZugferdInvoice(xmlStream, ZugferdProfile.XRechnung, PdfAttachmentRelationship.Alternative) and save. The XML is embedded as xrechnung.xml and the metadata marks the document as an XRechnung invoice.
The API is the same AddZugferdInvoice call. With ZugferdProfile.XRechnung the embedded file is named xrechnung.xml instead of factur-x.xml, and XRECHNUNG is recorded as the conformance level in the invoice metadata.
No. SelectPdf embeds and identifies the XML you supply. Producing it, and making sure its content matches the visible invoice, is your application's job.
Yes. Load it with new PdfDocument("invoice.pdf"), call ConvertToPdfA(PdfStandard.PdfA3B) unless it is already PDF/A-3, then call AddZugferdInvoice with the path of the XML file and ZugferdProfile.XRechnung, and save.