How do I create e-invoices via CSV and zugcmd from my own application?

Business terms
BT-1 Invoice number BT-2 Invoice issue date BT-3 Invoice type code BT-10 Buyer reference BT-72 Actual delivery date BT-106 Sum of Invoice line net amount BT-109 Invoice total amount without VAT BT-112 Invoice total amount with VAT
Code samples
Command line, Delphi
Published

In short: Your application – whether Delphi, VB6, Access, COBOL or any other language – writes the invoice data to four CSV files and calls the command-line tool zugcmd. zugcmd reads the files, creates the e-invoice XML according to EN 16931 (XRechnung 3.0, CII syntax) and embeds it into your existing PDF invoice. The result is a hybrid ZUGFeRD invoice (PDF/A-3) that you send by e-mail as usual. No programming against the SDK is needed.

The process

  1. Your application creates the PDF invoice as before.
  2. It writes the invoice data to the CSV files (see below).
  3. It starts zugcmd and evaluates the exit code (0 = success).
  4. zugcmd writes the XML and the ZUGFeRD PDF with the embedded XML.
  5. Your application sends the ZUGFeRD PDF by e-mail. zugcmd does not send it.

Without an existing PDF, zugcmd can also create the PDF from a Word template (DOCX); this is described on the zugcmd page.

Which CSV files does zugcmd need?

FileContentParameterRequired
seller.csvSeller: name, address, contact, VAT ID, bank details-syes – hardly changes, can stay in place as a master file
buyer.csvBuyer / invoice recipient-byes
items.csvInvoice lines, one row per line-pyes
totals.csvInvoice header, totals and VAT breakdown-tyes
allowances.csvCharges and allowances at document level-zonly if needed
deliverto.csv / deliverfrom.csvdifferent delivery or ship-from address-h / -gonly if needed

Structure of the CSV files

  • First row = column names, the data below. items.csv has one row per line; the other files have exactly one data row.
  • Mapping by column name: upper/lower case and the order of the columns do not matter. Simply leave out columns you do not need.
  • Amounts with a dot as the decimal separator (59.50), dates as YYYYMMDD.
  • Separator: specify it with -y (e.g. , or ;). Put values that contain the separator in quotes.
  • Character set: specify it with -c: the default is utf8; programs that write Windows ANSI (e.g. Delphi 5 to 2007, VB6) use -c ansi.
  • Several VAT rates: totals.csv holds up to 11 VAT rates – the first in the columns TAX…, further ones in TAX…1 to TAX…10 (e.g. TAXBasisAmount1).

Tested minimal example

An invoice for 10 × EUR 5.00 net at 19 %. With these four files the SDK creates an XML that the KoSIT validator (XRechnung 3.0.2) accepts without errors and without warnings (checked on 16 October 2026 with the function ImportCSV, which zugcmd also uses). The sample values are German, as in a real German invoice.

seller.csv

Name,personname,Street1,city,phone,email,vat,country,postcode,iban,bic,paymentmeans,paymentdescription,accountname
Muster GmbH,Erika Muster,Hauptstr. 1,Hamburg,+49 40 123456,rechnung@muster.example,DE123456789,DE,20095,DE02120300000000202051,BYLADEM1001,58,SEPA-Überweisung,Muster GmbH

buyer.csv

id,Name,Street1,city,email,country,postcode
K-1001,Kunde AG,Marktplatz 5,München,einkauf@kunde.example,DE,80331

items.csv

AssignedID,productname,description,quantity,unit,netprice,netbasisquantity,netbasisunit,taxpercent,taxtypecode,taxcategorycode,totalamount
ART-100,Papier A4,500 Blatt,10,C62,5.00,1,C62,19,VAT,S,50.00

totals.csv

type,currency,invoiceid,invoicedate,DeliveryDate,leitwegid,LineTotalAmount,ChargeTotalAmount,AllowanceTotalAmount,TaxBasisTotalAmount,TaxTotalAmount,GrandTotalAmount,TotalPrepaidAmount,DuePayableAmount,PaymentTerms,duedate,TAXCalculatedAmount,TAXTypeCode,TAXBasisAmount,TAXCategoryCode,TAXRateApplicablePercent
380,EUR,RE-2027-0001,20270104,20270104,K-1001,50.00,0.00,0.00,50.00,9.50,59.50,0.00,59.50,Zahlbar innerhalb 30 Tagen netto,20270203,9.50,VAT,50.00,S,19

The most important columns and their business terms

FileColumnMeaning
totals.csvtypeInvoice type, BT-3 (380 invoice, 381 credit note, 875 construction partial invoice)
totals.csvinvoiceid / invoicedateInvoice number BT-1 / invoice date BT-2
totals.csvcurrencyCurrency, BT-5
totals.csvDeliveryDateDelivery date, BT-72
totals.csvleitwegidBuyer reference or Leitweg-ID, BT-10
totals.csvPaymentTerms / duedatePayment terms BT-20 / due date BT-9
totals.csvLineTotalAmountSum of the lines, BT-106
totals.csvAllowanceTotalAmount / ChargeTotalAmountSum of allowances BT-107 / charges BT-108
totals.csvTaxBasisTotalAmountNet amount, BT-109
totals.csvTaxTotalAmountTotal VAT, BT-110
totals.csvGrandTotalAmount / DuePayableAmountGross amount BT-112 / amount due BT-115
totals.csvTAXBasisAmount / TAXCalculatedAmountTax basis BT-116 / tax amount BT-117 per VAT rate
totals.csvTAXCategoryCode / TAXRateApplicablePercentVAT category BT-118 (S, Z, E, AE, K …) / VAT rate BT-119
items.csvquantity / unitQuantity BT-129 / unit of measure BT-130 (e.g. C62 piece, H87 piece, KGM kilogram)
items.csvnetprice / netbasisquantity / netbasisunitNet price BT-146 per base quantity BT-149 in unit BT-150
items.csvtotalamountNet amount of the line, BT-131

All further columns (order number, project, contract, allowances per line, global article number …) are described on the pages of the individual files, linked in the table above (German).

For a valid XRechnung you also need the buyer reference or Leitweg-ID (leitwegid, BT-10), a seller contact with name, phone and e-mail (personname, phone, email) and an e-mail address for both parties.

Your application calculates the totals

zugcmd takes the totals exactly as they are in totals.csv. The most common errors:

  • Tax basis with allowances: TAXBasisAmount is the net amount after the allowances and charges of this VAT rate, not the sum of the lines – see BR-S-08, BR-S-09 and BR-CO-17.
  • Rounded lines: LineTotalAmount is the sum of the rounded line amounts (BR-CO-10).
  • Rounding only on the amount due: see BR-CO-15 and BR-CO-16.

Starting zugcmd from your own application

You do not have to start zugcmd by hand on the command line; your application calls it itself. Besides zugcmd.exe, the package contains zugcmd_wnd.exe, which runs without a console window – handy for calls from programs with a user interface. A call for one invoice (on one line):

zugcmd_wnd.exe -s "C:\ERechnung\stamm\seller.csv" -b "C:\ERechnung\RE-2027-0001\buyer.csv" -p "C:\ERechnung\RE-2027-0001\items.csv" -t "C:\ERechnung\RE-2027-0001\totals.csv" -i "C:\ERechnung\RE-2027-0001\RE-2027-0001.pdf" -x "C:\ERechnung\RE-2027-0001\RE-2027-0001.xml" -o "C:\ERechnung\RE-2027-0001\RE-2027-0001_zugferd.pdf" -y "," -c ansi -l YOUR-LICENCE-KEY

Always specify full paths (with drive and folder), or set a base directory with -d against which bare file names are resolved. A bare file name without -d is not found.

You recognise success by the exit code: 0 means success; examples of errors are 57 "input PDF not found" or 59 "XML could not be created". The complete list is on the zugcmd page. Functions such as ShellExecute (Delphi) or Shell (VB6) do not return the exit code; use CreateProcess with GetExitCodeProcess. Example for Delphi (version 5 and later):

uses Windows, SysUtils;

function StarteZugcmd(const Parameter: string): DWORD;
var
  SI: TStartupInfo;
  PI: TProcessInformation;
  Cmd: string;
begin
  Result := DWORD(-1);
  Cmd := '"C:\zugcmd\zugcmd_wnd.exe" ' + Parameter;
  FillChar(SI, SizeOf(SI), 0);
  SI.cb := SizeOf(SI);
  if CreateProcess(nil, PChar(Cmd), nil, nil, False, 0, nil, nil, SI, PI) then
  begin
    WaitForSingleObject(PI.hProcess, INFINITE);   // wait until zugcmd has finished
    GetExitCodeProcess(PI.hProcess, Result);      // 0 = success
    CloseHandle(PI.hThread);
    CloseHandle(PI.hProcess);
  end;
end;

File names and volumes

  • File names are set by your application with -x (XML) and -o (PDF), ideally using the invoice number. Without them, the files are called OutputXML.xml and OutputPDF.pdf; there is no automatic numbering.
  • Many invoices (e.g. a few hundred per day) are no problem: one call per invoice. Put the CSV files of each invoice in their own folder or give them unique names so that parallel calls do not overwrite each other.
  • Existing files are overwritten by zugcmd by default; with -f false they are kept.

Tip: look at the mapping on a real invoice

zugcmd also works in reverse: -r invoice.xml (or a ZUGFeRD PDF) splits an existing e-invoice into exactly these CSV files. That way you can see on a real example which field belongs in which column. The download package zugcmd.zip (downloads) also contains sample CSV files with the BT numbers written directly into the values, two complete sample sets (credit transfer, direct debit) and the readme.txt with all parameters.

If you prefer to program directly, use the SDK via COM: with Delphi or with VB6, VB5 and VBA.

Frequently asked questions

Do I need SDK programming skills to use zugcmd?

No. Your application writes CSV files and starts zugcmd with parameters. The only programming needed is writing the CSV files and the call.

Is there a description of the CSV columns?

Yes. Each file has its own page with all columns and business terms: seller.csv, buyer.csv, items.csv, totals.csv and allowances.csv. The columns are mapped by their names; the order is arbitrary.

Does zugcmd have to be started on the command line?

No. Any application can start zugcmd as a process. zugcmd_wnd.exe runs without a console window; exit code 0 means success.

How are the generated files named?

As your application specifies them with -x (XML) and -o (PDF), for example after the invoice number. Without them, they are called OutputXML.xml and OutputPDF.pdf.

Can zugcmd handle 200 invoices a day?

Yes. One call per invoice is enough. It is only important that each invoice has its own CSV and output files.

Does zugcmd also send the invoice by e-mail?

No. zugcmd creates the XML and the ZUGFeRD PDF; sending is done by your application or your e-mail program.

Business terms in this article: BT-1 BT-2 BT-3 BT-10 BT-72 BT-106 BT-109 BT-112 BT-115 BT-116 BT-117 BT-5 BT-9 BT-20 BT-107 BT-108 BT-110 BT-118 BT-119 BT-129 BT-130 BT-131 BT-146