Mapping a Stripe invoice to EN 16931, field by field
By evoicely · Last checked 19 October 2026
Most of an EN 16931 invoice can be filled from Stripe's Invoice object: number, dates, currency, customer, line items, VAT and totals. A few things an XRechnung or Peppol invoice needs have no Stripe field at all: the buyer reference (BT-10, where the Leitweg-ID goes), your own address, contact and IBAN, and a proper VAT category code. This page maps the fields one by one and shows the XML that comes out.
Field names follow Stripe's API reference for the Invoice, Invoice Line Item, Tax Rate, Tax ID and Credit Note objects. Business terms (BT-xx) and their UBL elements follow the Peppol BIS Billing 3.0 UBL syntax reference, which is built on EN 16931. For background on the formats, see XRechnung vs ZUGFeRD.
Three conversions before you map anything
- Amounts are integers in the smallest currency unit.
4900is €49.00. Zero-decimal currencies are the exception, so use Stripe's currency list instead of always dividing by 100. EN 16931 amounts are decimals. - Timestamps are Unix seconds. EN 16931 dates are calendar dates (
YYYY-MM-DDin UBL). Convert in the time zone you invoice in, or an invoice created late in the evening ends up dated the next day. - Currency codes are lowercase in Stripe (
eur). EN 16931 uses the ISO 4217 code, which in practice meansEUR.
Document level
| Stripe Invoice field | EN 16931 term | Notes |
|---|---|---|
number | BT-1 Invoice number | Null while the invoice is a draft. Map finalized invoices only. |
effective_at, else status_transitions.finalized_at | BT-2 Invoice issue date | Stripe says effective_at replaces the "Date of issue" printed on the PDF, and equals finalized_at unless overwritten. created is when the draft was created, which can be earlier. |
| (none) | BT-3 Invoice type code | 380 (commercial invoice) for invoices. Credit notes are covered below. |
currency | BT-5 Invoice currency code | Upper-case it. |
due_date | BT-9 Payment due date | Null when collection_method is charge_automatically. |
metadata or custom_fields | BT-10 Buyer reference | No native field. See the gaps. |
metadata or custom_fields | BT-13 Purchase order reference | No native field. Only needed if your customer gives you a PO number. |
description, footer | BT-22 Invoice note | description is the "memo" in the Dashboard. Both are free text. |
lines.data[].period | BT-73 / BT-74 Invoicing period | Not the Invoice's own period_start/period_end: Stripe describes those as the window for attaching invoice items and points to the line item period for the service period. |
Seller and buyer
| Stripe field | EN 16931 term | Notes |
|---|---|---|
account_name | BT-27 Seller name | Stripe's "public name of the business". Check it is your legal name. |
account_tax_ids | BT-31 Seller VAT identifier | An array of Tax ID object IDs. Expand it and use the value of the eu_vat entry. Often empty, see the gaps. |
customer_name | BT-44 Buyer name | Copied from the Customer and frozen at finalization, like the other customer_* fields. |
customer_tax_ids[].value | BT-48 Buyer VAT identifier | Pick the entry with type eu_vat. |
customer_email | BT-49 Buyer electronic address | Usable with scheme EM (email). For Peppol delivery you need the buyer's Peppol participant ID instead, which Stripe doesn't store natively either. |
customer_address.line1 | BT-50 Buyer address line 1 | |
customer_address.line2 | BT-51 Buyer address line 2 | |
customer_address.city | BT-52 Buyer city | |
customer_address.postal_code | BT-53 Buyer post code | |
customer_address.country | BT-55 Buyer country code | Already ISO 3166-1 alpha-2. |
Lines
Each entry in lines.data becomes an invoice line (BG-25).
| Stripe line item field | EN 16931 term | Notes |
|---|---|---|
description | BT-153 Item name | |
quantity_decimal (or quantity) | BT-129 Invoiced quantity | quantity is truncated to an integer; quantity_decimal keeps the precision. The unit code (BT-130) isn't in Stripe; C62 ("one") is a common choice for per-seat or per-plan lines. |
pricing.unit_amount_decimal | BT-146 Item net price | The old top-level price/unit_amount fields aren't on the current line item object. In Stripe's own example, unit_amount_decimal "1000" sits next to amount 1000 for quantity 1, so it's in the smallest currency unit too. |
amount, taxes[].taxable_amount | BT-131 Invoice line net amount | EN 16931 line amounts and prices are net. If taxes[].tax_behavior is inclusive, Stripe's amounts include VAT; use taxable_amount (the amount tax was calculated on). |
period.start, period.end | BT-134 / BT-135 Invoice line period | Both timestamps; Stripe documents both as inclusive. Check how your end timestamp converts to a date. |
taxes[].tax_rate_details.tax_rate → percentage | BT-152 Invoiced item VAT rate | Expand the Tax Rate to read percentage. |
taxes[].taxability_reason | BT-151 Invoiced item VAT category code | Not a one-to-one mapping. See VAT categories. |
discount_amounts[].amount | BG-27 Invoice line allowance (BT-136, reason BT-139/BT-140) | amount is before discounts. Stripe also spreads invoice-level coupons over the lines here, so line allowances cover every discount, and BT-131 becomes amount minus the allowances, which is what Stripe taxed. Reason code 95 (discount); expand discount to use the coupon's name as the reason text. |
VAT and totals
| Stripe Invoice field | EN 16931 term | Notes |
|---|---|---|
subtotal_excluding_tax | BT-106 Sum of invoice line net amounts | Must equal the sum of BT-131 (rule BR-CO-10). Recompute it from your lines and compare. With discounts it differs from Stripe's subtotal_excluding_tax, which is before discounts. |
total_excluding_tax | BT-109 Invoice total amount without VAT | Includes discounts. If you map discounts as line allowances (see Lines), BT-109 equals BT-106. Mapping invoice-level coupons as document-level allowances (BG-20) works too, but then you have to split them per VAT rate yourself. |
total_taxes[].taxable_amount, total_taxes[].amount | BT-116 / BT-117 per VAT category (BG-23) | Group by category and rate. |
sum of total_taxes[].amount | BT-110 Invoice total VAT amount | |
total | BT-112 Invoice total amount with VAT | Must equal BT-109 + BT-110 (BR-CO-15). |
amount_paid | BT-113 Paid amount | For card payments that Stripe has already collected. |
amount_remaining | BT-115 Amount due for payment | EN 16931 requires BT-115 = BT-112 − BT-113 + rounding (BR-CO-16). Stripe's amount_due can also reflect customer balance and credit, so derive BT-115 from the totals rather than copying it. |
Rounding. EN 16931 calculates VAT per category: BT-117 must equal BT-116 × rate ÷ 100, rounded to two decimals (BR-CO-17, a fatal error). If your tax amounts come from per-line calculations, the sum can be a cent off. Recalculate per category and check the result against Stripe's total before you issue the file. (EN 16931 rules in the Peppol BIS documentation)
What Stripe doesn't cover natively
Buyer reference and Leitweg-ID (BT-10)
XRechnung requires a buyer reference: rule BR-DE-15 makes BT-10 mandatory.
German public-sector buyers expect their Leitweg-ID there. Business customers often have none, so agree a reference with them, such as a PO or customer number.
Stripe has no field for this. Store it on the Customer, for example as a metadata key you choose such as leitweg_id, and copy it into each invoice.
If it should also be visible on the Stripe PDF, put it in the invoice's custom_fields (name/value pairs) as well.
(KoSIT XRechnung schematron)
Your own details: address, VAT ID and contact
The Invoice object gives you account_name and account_country, but no seller street, city or post code (BT-35 to BT-38).
account_tax_ids can be null (it is in Stripe's own example), so don't rely on it for your VAT ID.
XRechnung also requires a seller contact with name, phone and email (BT-41 to BT-43; rules BR-DE-2 and BR-DE-5 to BR-DE-7), which Stripe doesn't have.
Keep these in your own configuration and write them into every invoice.
Payment means and IBAN (BT-81, BT-84)
XRechnung requires payment instructions (BG-16, rule BR-DE-1). For a bank transfer, that's payment means code 58 (SEPA credit transfer) or 30 (credit transfer).
With either code, your account (BG-17, with the IBAN in BT-84) must be included (BR-DE-23-a).
The Invoice object has no field for your bank account; payment_settings.payment_method_types only lists the methods offered on Stripe.
For invoices Stripe has already charged to a card, record the payment as the paid amount (BT-113) so the amount due comes out at zero.
Delivery date and period (BT-72, BT-73/74)
EN 16931 has an actual delivery date (BT-72) and an invoicing period (BG-14: BT-73 start, BT-74 end).
Stripe has neither as an invoice field. For subscriptions, the line item periods are the natural source for the invoicing period.
For one-off services, decide where the date comes from, for example a metadata key.
VAT category codes (S, AE, Z, E)
Every line and every VAT breakdown needs a category code from UNTDID 5305. The ones Stripe users meet most:
SStandard rate, including reduced rates.AEReverse charge. Rate 0, and the breakdown needs the exemption reason "Reverse charge" as code (BT-121) or text (BT-120) (rule BR-AE-10).ZZero rated goods.EExempt from tax. Also needs an exemption reason code or text (BR-E-10).
Stripe gives you a taxability_reason per tax entry, with values such as standard_rated, reduced_rated, reverse_charge, zero_rated and product_exempt.
These are hints, not category codes. For example, Stripe's zero_rated covers both a 0% rate and some exempt transactions, which are different categories in EN 16931.
Which category applies is a VAT question for you and your tax advisor; the mapping should follow that decision, not guess it.
(UNCL5305 code list)
Credit notes (BT-3 381, BT-25)
Stripe issues corrections as separate Credit Note objects. Map them like this:
- BT-3 is
381(credit note). UBL has a separateCreditNotedocument for this, withCreditNoteTypeCode381 andCreditNoteLineelements. - BT-1 is the credit note's
number, and BT-2 is itseffective_at, which defaults tocreated. - BT-25 (preceding invoice reference) is the
numberof the invoice ininvoice. Expand it to read the number. BT-26 is that invoice's issue date. - Lines come from the credit note's
lines, with their ownquantity,unit_amount_decimalandtaxes.
What the result looks like
This is an excerpt of an XRechnung 3.0 invoice in UBL syntax, generated from fictional data by the same code evoicely uses.
The complete file passes the official KoSIT validator with the XRechnung configuration in our test suite.
The <!-- --> comments were added for this page.
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" ...>
<cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</cbc:CustomizationID>
<cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>INV-2026-0001</cbc:ID> <!-- BT-1 number -->
<cbc:IssueDate>2026-10-01</cbc:IssueDate> <!-- BT-2 effective_at -->
<cbc:DueDate>2026-10-15</cbc:DueDate> <!-- BT-9 due_date -->
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode> <!-- BT-3 -->
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode> <!-- BT-5 currency, upper-cased -->
<cbc:BuyerReference>PO-4711</cbc:BuyerReference> <!-- BT-10 from metadata -->
<cac:InvoicePeriod> <!-- BT-73/74 from line periods -->
<cbc:StartDate>2026-10-01</cbc:StartDate>
<cbc:EndDate>2026-10-31</cbc:EndDate>
</cac:InvoicePeriod>
<!-- AccountingSupplierParty: your own details, incl. VAT ID and contact -->
<cac:AccountingCustomerParty>
<cac:Party>
<cbc:EndpointID schemeID="EM">invoices@kunde-handels.example</cbc:EndpointID>
<cac:PostalAddress>
<cbc:StreetName>Hauptstraße 5</cbc:StreetName> <!-- BT-50 customer_address.line1 -->
<cbc:CityName>München</cbc:CityName> <!-- BT-52 -->
<cbc:PostalZone>80331</cbc:PostalZone> <!-- BT-53 -->
<cac:Country>
<cbc:IdentificationCode>DE</cbc:IdentificationCode> <!-- BT-55 -->
</cac:Country>
</cac:PostalAddress>
<cac:PartyTaxScheme>
<cbc:CompanyID>DE987654321</cbc:CompanyID> <!-- BT-48 customer_tax_ids[] -->
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:PartyTaxScheme>
<cac:PartyLegalEntity>
<cbc:RegistrationName>Kunde Handels AG</cbc:RegistrationName> <!-- BT-44 customer_name -->
</cac:PartyLegalEntity>
</cac:Party>
</cac:AccountingCustomerParty>
<cac:PaymentMeans>
<cbc:PaymentMeansCode>58</cbc:PaymentMeansCode> <!-- BT-81 SEPA credit transfer -->
<cac:PayeeFinancialAccount>
<cbc:ID>DE02120300000000202051</cbc:ID> <!-- BT-84 your IBAN -->
</cac:PayeeFinancialAccount>
</cac:PaymentMeans>
<cac:PaymentTerms>
<cbc:Note>Payable within 14 days without deduction.</cbc:Note> <!-- BT-20 -->
</cac:PaymentTerms>
<cac:TaxTotal>
<cbc:TaxAmount currencyID="EUR">15.00</cbc:TaxAmount> <!-- BT-110 -->
<cac:TaxSubtotal>
<cbc:TaxableAmount currencyID="EUR">78.97</cbc:TaxableAmount> <!-- BT-116 -->
<cbc:TaxAmount currencyID="EUR">15.00</cbc:TaxAmount> <!-- BT-117 -->
<cac:TaxCategory>
<cbc:ID>S</cbc:ID> <!-- BT-118 -->
<cbc:Percent>19</cbc:Percent> <!-- BT-119 -->
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:TaxCategory>
</cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
<cbc:LineExtensionAmount currencyID="EUR">78.97</cbc:LineExtensionAmount> <!-- BT-106 -->
<cbc:TaxExclusiveAmount currencyID="EUR">78.97</cbc:TaxExclusiveAmount> <!-- BT-109 -->
<cbc:TaxInclusiveAmount currencyID="EUR">93.97</cbc:TaxInclusiveAmount> <!-- BT-112 -->
<cbc:PayableAmount currencyID="EUR">93.97</cbc:PayableAmount> <!-- BT-115 -->
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
<cbc:ID>1</cbc:ID>
<cbc:InvoicedQuantity unitCode="C62">1</cbc:InvoicedQuantity> <!-- BT-129 -->
<cbc:LineExtensionAmount currencyID="EUR">49.00</cbc:LineExtensionAmount> <!-- BT-131 -->
<cac:InvoicePeriod> <!-- BT-134/135 period.start/end -->
<cbc:StartDate>2026-10-01</cbc:StartDate>
<cbc:EndDate>2026-10-31</cbc:EndDate>
</cac:InvoicePeriod>
<cac:Item>
<cbc:Name>Pro plan (monthly)</cbc:Name> <!-- BT-153 description -->
<cac:ClassifiedTaxCategory>
<cbc:ID>S</cbc:ID> <!-- BT-151 -->
<cbc:Percent>19</cbc:Percent> <!-- BT-152 -->
<cac:TaxScheme>
<cbc:ID>VAT</cbc:ID>
</cac:TaxScheme>
</cac:ClassifiedTaxCategory>
</cac:Item>
<cac:Price>
<cbc:PriceAmount currencyID="EUR">49</cbc:PriceAmount> <!-- BT-146 net unit price -->
</cac:Price>
</cac:InvoiceLine>
<!-- second line: 3 × "Extra seats" at 9.99 -->
</Invoice>Where to go from here
- Does Stripe support e-invoicing?: what Stripe does itself and the integration options.
- Germany's e-invoicing mandate for Stripe users: who has to send e-invoices, and from when.
- XRechnung vs ZUGFeRD: which format to send.
- ZUGFeRD profiles explained: which profiles count as e-invoices in Germany.
- What is a Leitweg-ID: the buyer reference (BT-10) for public-sector customers, and how to store it in Stripe.
Where evoicely fits
evoicely is being built to do this mapping for you: it reads your Stripe invoices and credit notes, fills the gaps from settings you set once, and produces ZUGFeRD and XRechnung, then Peppol BIS 3.0, each checked with the official validators. It's planned, not available yet. Join the waitlist.
This is general information, not legal or tax advice. Check what applies to your business with your tax advisor.