Skip to main content
Version: Canary 🚧

Identifiers

The id module creates typed identifiers of parties: a registration or location number together with the scheme that says what it is. Give them wherever a party identifier is accepted, and the e-invoice (ZUGFeRD / Factur-X / XRechnung) states the identifier with its scheme and checks its format and check digit.

sender: (
...
legal-id: id.siret("123 456 782 00010"), // BT-30, scheme 0009
)

Identifiers are optional: an invoice without them needs none of this. Plain text is accepted as well (e.g. legal-id: "HRB 4711"), and identifiers of the id module are only checked for an e-invoice. They are not printed by the built-in themes; see Printing identifiers.


Constructors​

ConstructorIdentifierSchemeCheckedExample
id.gln(value)Global Location Number of GS1 (a company or a location)008813 digits, GS1 check digitid.gln("4000001123452")
id.duns(value)D-U-N-S number of Dun & Bradstreet00609 digitsid.duns("15-048-3782")
id.siren(value)SIREN of a French company00029 digits, Luhn check digitid.siren("123 456 782")
id.siret(value)SIRET of a French establishment000914 digits, Luhn check digit (La Poste: its own check)id.siret("123 456 782 00010")
id.uid-ch(value)Swiss enterprise identification number (UID)0183"CHE" and 9 digits, check digit modulo 11id.uid-ch("CHE-123.456.788")
id.register(value, court: none)number in a commercial register without a scheme (e.g. German HRB)nonenot emptyid.register("HRB 4711", court: "Amtsgericht München")
id.leitweg(value)Leitweg-ID of a German public buyer0204 (EAS)up to 12 digits, part of up to 30 capital letters and digits, check digits (MOD 97-10)id.leitweg("04011000-1234512345-06")
id.custom(scheme, id)any other identifier with the code of its schemeas givennothing: the escape hatchid.custom("0208", "0123456749") (Belgian enterprise number)
  • Spaces, dots and hyphens that numbers are often grouped with are removed ("123 456 782 00010" is "12345678200010"); the hyphens of a Leitweg-ID are part of it.
  • id.uid-ch also takes the UID with the suffix of a Swiss VAT number ("MWST", "TVA" or "IVA") or of the commercial register ("HR" or "RC"): the suffix is dropped, as the UID is the same.
  • id.register writes the court in front of the number, e.g. "Amtsgericht München, HRB 4711": a register number is unique only within its register, and EN 16931 has no field of its own for the court.
  • The scheme codes are those of ISO/IEC 6523 (ICD), the Leitweg-ID's of the electronic address schemes (EAS). The e-invoice checks the scheme of id.custom against the code list of the field it is given for (BR-CL-10, BR-CL-11, BR-CL-25).

Where Identifiers Go​

InputBusiness termTakes
sender.legal-id, recipient.legal-idlegal registration identifier (BT-30, BT-47)id.siren, id.siret, id.uid-ch, id.register, id.custom, text
sender.id / global-id, recipient.id / global-idseller and buyer identifier (BT-29, BT-46)id.gln, id.duns, id.siret, id.custom, text
delivery-address.id / global-iddeliver-to location identifier (BT-71)id.gln, id.custom, text
payee.id / global-id, payee.legal-idpayee identifier and legal registration (BT-60, BT-61)as for the seller and buyer
recipient.leitweg-id or recipient.buyer-referencebuyer reference (BT-10)id.leitweg, text
electronic-address of sender and recipientelectronic address (BT-34, BT-49)id.leitweg, id.gln, id.siret, id.custom, text

An identifier with a scheme given as id is the global identifier (ram:GlobalID), the same as global-id. The legal registration identifier is part of every profile, MINIMUM included, where it is the seller's identifier besides its VAT identifier (BR-CO-26).

An identifier that produces no text stops the compilation with the input it was given for, with and without e-invoice, as it would be missing from the printed invoice and the XML without notice: a constructor given without calling it (legal-id: id.siret instead of legal-id: id.siret("..")), or a dictionary whose id is missing or empty (legal-id: (scheme: "0002")). An empty value such as "" or none (e.g. an empty field of imported data) counts as not given, and an electronic address without id is derived from the VAT identifier or the email address instead.

Problems​

A constructor never stops the compilation. What is wrong with an identifier is kept as its problems, and an e-invoice reports them as errors with the input they were given for:

1. [IP-ID-01] sender.legal-id: The check digit of the SIRET "123 456 782 00011" is wrong.
Hint: Check the identifier for typos. If it is right as it is, give it with `id.custom("0009", ..)`, which does not check it.
RuleChecks
IP-ID-01The format and check digit of an identifier of the id module.
IP-ID-03An identifier given for a field it does not belong to: a Leitweg-ID as party identifier or legal registration identifier, another identifier as leitweg-id, a GLN or D-U-N-S number as legal-id, or a register number as id.

id.custom(scheme, id) takes any identifier unchecked: use it for schemes without a constructor, and for an identifier you know to be right although its check digit is rejected.

Printing Identifiers​

The built-in themes do not print the legal registration identifier, the trading name or the legal information of a party. Where the law requires them on the invoice (e.g. the register and register court of a German GmbH, the SIREN or SIRET of a French company), print them from the same value, so that the printed invoice and the e-invoice cannot differ:

#import "@preview/invoice-pro:0.5.0": *

#let register = id.register("HRB 98765", court: "Amtsgericht München")

#show: invoice.with(
zugferd: "en16931",
sender: (
name: "Tech Solutions GmbH",
address: "Software Allee 10",
city: "80331 München",
country: country.de,
vat-id: "DE123456788",
legal-id: register,
contact: (
name: "Max Mustermann",
phone: "+49 89 123456",
email: "rechnung@techsol.example",
),
// Printed by the DIN 5008 theme next to the sender address
extra: (("Handelsregister", register.id),),
),
recipient: (
name: "Kunde GmbH",
address: "Domstraße 5",
city: "50667 Köln",
country: country.de,
vat-id: "DE987654321",
legal-id: id.register("HRB 12345", court: "Amtsgericht Köln"),
),
invoice-nr: "RE-2026-0101",
date: datetime(year: 2026, month: 9, day: 1),
)

#line-items[
#item([Consulting], quantity: 8, unit: unit.hour, price: 120)
]
#payment-goal(days: 14)
#bank-details(iban: "DE89370400440532013000", bic: "COBADEFFXXX")

In the text of the invoice, #info.sender.legal-id prints the identifier of the sender.