Getting started with FactoorSharp
With FactoorSharp you can create, read and validate electronic invoices (ZUGFeRD, XRechnung, EN16931) in just a few minutes.
This guide walks you step by step to your first working invoice.
By the end of this quick start you'll be able to:
- create an invoice in C#
- export it as EN16931 / ZUGFeRD XML
- optionally embed it in a PDF/A-3
- validate the result
Installation
Install FactoorSharp via NuGet:
dotnet add package FactoorSharp.FacturX
Or via the Package Manager:
Install-Package FactoorSharp
The library supports: .NET 6+, .NET Framework 4.6, .NET Framework 4.8 (depending on your target platform)
Get a license
TODO: Sign up and retrieve
Create your first invoice
In the following example you'll create a minimal EN16931-compliant invoice.
First, the invoice header data with the seller and buyer identification. This also includes the delivery details:
FacturXInvoice.SetLicense("...");
var invoice = FacturXInvoice.CreateInvoice("Invoice-01",
new DateTime(2025,09,03),
Currency.EUR)
// BG-14
.SetSeller(name: "BikeTech GmbH",
postcode: "10115",
city: "Berlin",
street: "Radweg 12",
country: CountryCodes.DE,
id: String.Empty,
globalID: new GlobalID(GlobalIDSchemeIdentifiers.GLN, "4000001123452"),
legalOrganization: new LegalOrganization(GlobalIDSchemeIdentifiers.GLN, "4000001123452", "BikeTech GmbH"))
// BG-7
.SetBuyer(name: "CityRider AG",
postcode: "20457",
city: "Hamburg",
street: "Hafenstraße 45",
country: CountryCodes.DE,
id: "DE987654321");
// BT-31
invoice.AddSellerTaxRegistration("201/113/40209", TaxRegistrationSchemeID.FC);
// BT-72 - ActualDeliverySupplyChainEvent
invoice.ActualDeliveryDate = DateTime.Today;
// alternative: billing period (BG-14)
// invoice.SetBillingPeriod(
// billingPeriodStart: DateTime.Today.AddDays(-7),
// billingPeriodEnd: DateTime.Today);
FacturXInvoice.SetLicense("...")
Dim invoice = FacturXInvoice.CreateInvoice("Invoice-01",
New DateTime(2025, 9, 3),
Currency.EUR) _
.SetSeller(name:="BikeTech GmbH",
postcode:="10115",
city:="Berlin",
street:="Radweg 12",
country:=CountryCodes.DE,
id:=String.Empty,
globalID:=New GlobalID(GlobalIDSchemeIdentifiers.GLN, "4000001123452"),
legalOrganization:=New LegalOrganization(GlobalIDSchemeIdentifiers.GLN, "4000001123452", "BikeTech GmbH")) _
.SetBuyer(name:="CityRider AG",
postcode:="20457",
city:="Hamburg",
street:="Hafenstraße 45",
country:=CountryCodes.DE,
id:="DE987654321")
' BT-31
invoice.AddSellerTaxRegistration("201/113/40209", TaxRegistrationSchemeID.FC)
' BT-72 - ActualDeliverySupplyChainEvent
invoice.ActualDeliveryDate = DateTime.Today
' alternative: billing period (BG-14)
' invoice.SetBillingPeriod(
' billingPeriodStart:=DateTime.Today.AddDays(-7),
' billingPeriodEnd:=DateTime.Today)
Next, we add a line item:
invoice.AddTradeLineItem(name: "Trekking bike",
netUnitPrice: 799.0m,
unitCode: QuantityCodes.C62,
grossUnitPrice: 799.0m,
billedQuantity: 1,
taxType: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S,
taxPercent: 19,
sellerAssignedID: "BIKE-1");
invoice.AddTradeLineItem(name:="Trekking bike",
netUnitPrice:=799.0D,
unitCode:=QuantityCodes.C62,
grossUnitPrice:=799.0D,
billedQuantity:=1,
taxType:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S,
taxPercent:=19,
sellerAssignedID:="BIKE-1")
And the totals block including payment terms:
decimal taxTotalAmount = 799.0m / 100m * 19m;
invoice.AddApplicableTradeTax(basisAmount: 799.0m,
percent: 19m,
taxAmount: taxTotalAmount,
typeCode: TaxTypes.VAT,
categoryCode: TaxCategoryCodes.S);
invoice.PaymentTerms.Add(new PaymentTerms() // BT-20
{
DueDate = DateTime.Today.AddDays(14)
});
invoice.SetTotals(lineTotalAmount: 799.0m,
taxBasisAmount: 799.0m,
taxTotalAmount: taxTotalAmount,
grandTotalAmount: 799.0m + taxTotalAmount,
duePayableAmount: 799.0m + taxTotalAmount);
invoice.Save("e:\\factur-x.xml",
version: ZUGFeRDVersion.Version23,
profile: Profile.Extended);
Dim taxTotalAmount As Decimal = 799.0D / 100D * 19D
invoice.AddApplicableTradeTax(basisAmount:=799.0D,
percent:=19D,
taxAmount:=taxTotalAmount,
typeCode:=TaxTypes.VAT,
categoryCode:=TaxCategoryCodes.S)
invoice.PaymentTerms.Add(New PaymentTerms() With ' BT-20
{
.DueDate = DateTime.Today.AddDays(14)
})
invoice.SetTotals(lineTotalAmount:=799.0D,
taxBasisAmount:=799.0D,
taxTotalAmount:=taxTotalAmount,
grandTotalAmount:=799.0D + taxTotalAmount,
duePayableAmount:=799.0D + taxTotalAmount)
invoice.Save("e:\factur-x.xml",
version:=ZUGFeRDVersion.Version23,
profile:=Profile.Extended)
Export as XML (EN16931 / XRechnung / ZUGFeRD)
Once your invoice is built, you can export it as XML:
invoice.Save("factur-x.xml",
version: ZUGFeRDVersion.Version23,
profile: Profile.Extended);
invoice.Save("factur-x.xml",
version:=ZUGFeRDVersion.Version23,
profile:=Profile.Extended)
Typical profiles used for ZUGFeRD are:
- Comfort / EN16931
- Standard for the EU
- Extended
- The most detailed and widely used profile in Germany
Validate an invoice
To make sure all required fields are present, you can validate your invoice using the validation tool in the customer area at factoorsharp.de.
This helps your invoice recipients process it smoothly.
Create a PDF/A-3 with embedded invoice data (ZUGFeRD)
If you already have a PDF file, for example from a report generator or your ERP system, you can embed the ZUGFeRD information directly into it:
await FacturXInvoicePdfProcessor.SaveToPdfAsync(
"FacturX-invoice.pdf",
ZUGFeRDVersion.Version23,
Profile.Extended,
ZUGFeRDFormats.CII,
"Classic-PDF-invoice.pdf");
Await FacturXInvoicePdfProcessor.SaveToPdfAsync(
"FacturX-invoice.pdf",
ZUGFeRDVersion.Version23,
Profile.Extended,
ZUGFeRDFormats.CII,
"Classic-PDF-invoice.pdf")
Result: a visually readable PDF/A-3 file that also contains the machine-readable XML data embedded in the PDF document.
Read existing invoices
FactoorSharp can also analyze existing invoices. It doesn't matter whether you're reading an XML file:
var invoice = FacturXInvoice.CreateInvoice("Invoice-01",
new DateTime(2025,09,03),
Currency.EUR)
.SetSeller("BikeTech GmbH", "10115", "Berlin", ...)
.SetBuyer("City Bikes AG", "20457", "Hamburg", ...);
invoice.AddTradeLineItem("Trekking bike",
799.0m,
QuantityCodes.C62,
1);
invoice.Save("factur-x.xml",
version: ZUGFeRDVersion.Version23,
profile: Profile.Extended);
Dim invoice = FacturXInvoice.CreateInvoice("Invoice-01",
New DateTime(2025, 9, 3),
Currency.EUR) _
.SetSeller("BikeTech GmbH", "10115", "Berlin", ...) _
.SetBuyer("City Bikes AG", "20457", "Hamburg", ...)
invoice.AddTradeLineItem("Trekking bike",
799.0D,
QuantityCodes.C62,
1)
invoice.Save("factur-x.xml",
version:=ZUGFeRDVersion.Version23,
profile:=Profile.Extended)
Or need to extract the XML file from a PDF file:
var invoice = FacturXInvoice.CreateInvoice("Invoice-01",
new DateTime(2025,09,03),
Currency.EUR)
.SetSeller("BikeTech GmbH", "10115", "Berlin", ...)
.SetBuyer("City Bikes AG", "20457", "Hamburg", ...);
invoice.AddTradeLineItem("Trekking bike",
799.0m,
QuantityCodes.C62,
1);
invoice.Save("factur-x.xml",
version: ZUGFeRDVersion.Version23,
profile: Profile.Extended);
Dim invoice = FacturXInvoice.CreateInvoice("Invoice-01",
New DateTime(2025, 9, 3),
Currency.EUR) _
.SetSeller("BikeTech GmbH", "10115", "Berlin", ...) _
.SetBuyer("City Bikes AG", "20457", "Hamburg", ...)
invoice.AddTradeLineItem("Trekking bike",
799.0D,
QuantityCodes.C62,
1)
invoice.Save("factur-x.xml",
version:=ZUGFeRDVersion.Version23,
profile:=Profile.Extended)
Migrating from ZUGFeRD-csharp
FactoorSharp is the successor to ZUGFeRD-csharp.
If you're already working with it:
- The API is deliberately kept similar
- Your existing logic can usually be carried over directly
- Replace InvoiceDescriptor with FacturXInvoice, and InvoicePdfProcessor with FacturXInvoicePdfProcessor. That's it.
- New features: better validation, new capabilities