Structured invoicing works because a machine can decide what a value means without asking anybody. That is only possible where values come from published lists, and it is the reason so much of an e-invoicing implementation is spent on data that nobody thought was interesting.

There are two related mechanisms, and they are frequently confused.

Code lists

A code list is a fixed set of permitted values for a term. The invoice does not say "each" or "kilogram" or "standard rate"; it carries a code, and the receiver looks the code up.

The lists that matter most in practice:

  • Country codes. Two-letter, from an international standard. Rarely a problem, occasionally a surprise where a territory's treatment differs from its country's.
  • Currency codes. Three-letter, from an international standard. Note that the currency of the invoice and the currency in which tax is accounted for are different terms and may legitimately differ.
  • Unit of measure codes. A large international list, and a reliable source of pain. Item masters typically hold free text — "box", "pallet", "hr" — which is not a code and cannot be turned into one automatically.
  • Tax category codes. A small list distinguishing standard rate, zero rate, exempt, reverse charge, out of scope and a handful of others. Getting these wrong produces documents that validate and misstate the treatment.
  • Allowance and charge reason codes. Why a discount or a surcharge exists. Buyers use them for automatic handling, and a free-text reason where a code is expected defeats that.
  • Document type codes. What kind of document this is. Discussed for one national case in Italy's national schema, and general.
  • Payment means codes. How payment is expected. Matters more than it looks, because it drives the buyer's payment run.

Identifier schemes

An identifier scheme is different. It is a register that issues identifiers, plus a code that names the register.

That naming code is the part people omit, and omitting it makes the identifier unusable. A thirteen-digit number could be a location identifier, a company registration number or a national tax number. The scheme code says which register to resolve it in.

An electronic address taken apart into its scheme identifier and its participant value.
An electronic address taken apart into its scheme identifier and its participant value.

Two consequences that cost real money.

An onboarding form asking for "your company number" collects nothing usable. It has to ask for the identifier and the scheme, and the person filling it in has to know which register their number came from. Most do not. This is why supplier and customer onboarding is a data-collection exercise with a validation step, not a mail merge.

The same organisation can be addressable under more than one scheme. They are not interchangeable, and sending the right value under the wrong scheme resolves to nobody or to somebody else. See addressing on Peppol.

Where each list bites

Where the effort actually falls
ListHeld inTypical state before a projectEffort to fix
Country and currencyMaster dataUsually correctHours
Unit of measureItem masterFree text, inconsistent, sometimes blankWeeks to months
Tax categoryTax configuration or item masterPresent but incomplete at line levelWeeks
Allowance and charge reasonPricing configurationFree textDays
Payment meansCustomer master or payment termsImplicit in a text fieldDays
Identifier schemesCustomer and supplier masterAbsent entirelyMonths, because it needs counterparty input

Two rows dominate. Units of measure are painful because item masters are large, old and were never validated against anything. Identifier schemes are painful because the data is not yours: somebody at the other company has to supply it, and chasing several thousand counterparties for a field they do not understand is a campaign, not a task.

Everything else on that list is a few days of work by somebody who knows the systems.

The versioning trap

Code lists are versioned, and so are the subsets that profiles permit. A value can be:

  • valid in the underlying international list, and not permitted by your profile;
  • permitted last year, deprecated this year, rejected next year;
  • permitted in one country's profile and forbidden in another's.

A document that passed a year ago and fails today, with no change on your side, is almost always a code list version. The fix is trivial; finding it is not, unless somebody is following the release notes. That responsibility belongs with whoever owns exception handling, and it should be written down rather than assumed.

The most common quiet error

Populating a mandatory code field with a constant because the project needed to get past validation. It works, it ships, and it is wrong on every document thereafter. If you cannot source a value honestly, that is a finding to escalate, not a default to hard-code.

Why this is the first thing to do

Three reasons, and together they make an unusually strong case.

It needs no decisions. Cleaning unit of measure codes does not require you to have chosen a format, a provider or an architecture. Nothing downstream can invalidate the work.

It has the longest lead time. Counterparty data arrives at the speed your counterparties reply, which is not a speed you control.

It determines your exception rate. The proportion of invoices that fail — and therefore the size of the operation you will be running from go-live — is decided almost entirely by data quality, not by software. That is the argument made in where the cost lands, and it is the strongest reason to start here.

The vocabulary for describing what is missing is in business terms and business groups. Run last month's invoices against your profile's required terms, list every field that cannot be populated from data you hold today, and you have a scope document that no vendor questionnaire will improve on.