Skip to content

Gapless invoice numbers: what the GoBD require

A sequential number sounds trivial. In a web app with concurrent requests, drafts and rollbacks, it is not. What the law requires and how to build it properly.

Management15 Oct 2026 · about 10 minutes

In short

German VAT law requires a sequential number that is assigned only once; several number ranges are allowed. According to the VAT application decree, a gapless sequence is not mandatory, but gaps must be explainable, because the GoBD require completeness and traceability. Gapless numbers are the simplest proof. Technically: assign the number only when issuing, inside a locked transaction, and never change it afterwards.

What the German VAT Act requires

Under sec. 14 (4) sentence 1 no. 4 UStG, every invoice must carry a sequential number, with one or more series of numbers, that the issuer assigns only once to identify the invoice.

  • Unique: the same number may exist only once in the company, across all brands, branches and systems.
  • Sequential: the numbers follow a recognisable order. Letters and prefixes such as RE-2026- are allowed.
  • Several number ranges: allowed, e.g. one per year, location or document type, as long as every number stays unique.

The VAT application decree (UStAE) states that a gapless sequence is not mandatory. That does not mean gaps don’t matter.

What the GoBD say

The GoBD, the German principles for keeping books and records electronically, require among other things:

  • Completeness: all transactions are recorded. A missing invoice number raises the question whether an invoice has disappeared.
  • Traceability: a knowledgeable third party must be able to follow what happened within a reasonable time.
  • Immutability: a record must not be changed in a way that the original content can no longer be determined (sec. 146 (4) AO).
  • Retention: invoices are accounting records and are kept for 8 years (sec. 147 AO, sec. 14b UStG).

In practice

In a tax audit, gaps in the number range are a classic reason for questions. Without gaps, there is nothing to explain. With gaps, you need documentation of why.

Typical mistakes when building it yourself

Most problems are not caused by bad intent but by technology built for something else:

ApproachProblem
MAX(number) + 1Two concurrent requests read the same value and assign the same number
Auto-increment ID as invoice numberDatabases consume IDs in rolled-back transactions too; gaps appear
Assigning the number to draftsDeleted or discarded drafts leave gaps
Deleting instead of cancellingA gap and a missing document at once
Separate numbers per brand or systemDuplicate numbers within the same company
Yearly reset without the year in the number0001 exists every year; the number is no longer unique
Changing the number after issuingViolates immutability

The most common mistake looks harmless:

IssueInvoice.php
// race condition: two processes read the same MAX()
$last = Invoice::max('sequence');
$invoice->sequence = $last + 1;
$invoice->number = sprintf('RE-2026-%04d', $invoice->sequence);
$invoice->save();

How to build it properly

Four rules are enough for safe numbers:

  1. Assign the number only when issuing. Drafts have no number, so they can be deleted without leaving a gap.
  2. Inside a transaction with a lock. The counter is locked (SELECT … FOR UPDATE), increased and saved together with the invoice. If anything fails, everything rolls back and the number is free again.
  3. Enforce uniqueness in the database. A unique index on the number is the last line of defence.
  4. Never change or delete. Fix mistakes with a cancellation or correction, not by editing.

A sketch with Laravel tools:

IssueInvoice.php
use Illuminate\Support\Facades\DB;

// create the counter row if it is missing (outside the transaction)
DB::table('invoice_sequences')->insertOrIgnore(['key' => 'invoice:2026', 'value' => 0]);

DB::transaction(function () use ($invoice) {
    // lock the counter: concurrent processes wait here
    $sequence = DB::table('invoice_sequences')->where('key', 'invoice:2026')->lockForUpdate()->first();
    $next = $sequence->value + 1;

    DB::table('invoice_sequences')->where('key', 'invoice:2026')->update(['value' => $next]);

    $invoice->number = sprintf('RE-2026-%04d', $next);
    $invoice->save();                              // if this fails, the counter rolls back too
}, 3);

The details make it hard

Deadlocks and serialisation failures must be retried, SQLite needs a different transaction mode, backdated invoices belong to the counter of their year, and several brands of one company must share a number space. That is exactly what Quittio Managed takes care of.

With Quittio Managed

Quittio Managed assigns numbers from a pattern and a counter. With the German defaults, the invoices of 2026 are numbered RE-2026-0001, RE-2026-0002 and so on; the counter starts again in 2027.

config/quittio-managed.php
// config/quittio-managed.php
'numbering' => [
    'pattern' => 'RE-{Y}-{seq:4}',          // null = country pack (DE: RE-{Y}-{seq:4})
    'reset' => 'yearly',                    // never | yearly | monthly
    'start' => 1,
    'per_kind' => [
        'cancellation' => 'ST-{Y}-{seq:4}', // own range for cancellations, null = shared
        'correction' => null,
    ],
],
PlaceholderReplaced by
{Y}, {y}Year of the issue date, four or two digits
{m}Month, two digits
{seq}, {seq:N}Running number, padded to N digits
{issuer}Key of the issuer profile
{customer}Buyer identifier (BT-46)

The number is created only by issue(), inside the issuing transaction:

InvoiceService.php
$draft = Invoice::draft(Quittio::invoice()->buyer($customer)->line('Consulting', netPrice: '120.00'));
$draft->number;                // null: drafts have no number

$invoice = $draft->issue();
$invoice->number;              // RE-2026-0001
  • Drafts, validation errors and failed cancellations use no number; a rollback gives it back.
  • Concurrent issues are serialised with a row lock; deadlocks and lock conflicts are retried up to three times.
  • Unique indexes on number space and number add another safeguard.
  • Patterns are checked: a yearly reset without {Y}, or two ranges that could produce the same number, are rejected.
  • Backdated invoices use the counter of their period: an invoice dated 30 Dec 2026 and issued in January 2027 gets the next number of 2026.

Several brands, one company

Numbers must be unique per company. If a company has several issuer profiles, e.g. one per brand, they share a number space in Quittio Managed. By default it follows from the VAT ID or tax number, so profiles of the same company belong together automatically. A number one brand used is blocked for the others, so give the brands different patterns, for example with {issuer}.

Moving from another system

Coming from an existing system, set the counter once to the next value. Counters can only go up, never down, and every change is written to the audit log:

Terminal
php artisan quittio:sequence:show
php artisan quittio:sequence:set default:invoice:2026 1043

Frequently asked questions

Must invoice numbers be gapless?

According to the German VAT application decree, a gapless sequence is not mandatory. Gaps must be explainable, though, because the GoBD require completeness and traceability. Gapless numbers save you that explanation.

May I have several number ranges?

Yes, e.g. per year, location or document type. Every number must still be unique within the company.

May the number contain letters?

Yes. Prefixes like RE- or the year are common and allowed, as long as the number stays unique.

Does the counter have to restart every year?

No, that is a convention. If it restarts, the year must be part of the number, otherwise numbers repeat. Quittio Managed rejects such patterns.

What do I do if a gap has occurred?

Document why, e.g. a technical error with date and affected number. And change the assignment so numbers are created only when issuing, inside a transaction.

Do cancellation invoices get their own number?

Yes, every cancellation invoice is an invoice of its own with its own number. In Quittio Managed it shares the range with invoices or gets its own, e.g. ST-{Y}-{seq:4}.

As of: 15 Oct 2026. Not tax or legal advice.

Sources: Sec. 14 (4) UStG · VAT application decree (UStAE), section 14.5 (10) · Sec. 146 (4) AO · Sec. 147 AO · GoBD (Federal Ministry of Finance letter of 28 Nov 2019, last amended on 14 Jul 2025)

Numbers that are simply right.

Quittio Managed assigns gapless numbers, safe under concurrent issuing.