---
title: "Number invoices in one unbroken sequence · Agent Wranglers"
url: https://agentwranglers.se/playbook/payments/invoices-and-records/sequential-invoice-numbers
updated: 2026-10-20
licence: "All rights reserved"
---

# Number invoices in one unbroken sequence

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [Invoices and records](https://agentwranglers.se/playbook/payments/invoices-and-records)
- Rule: Give each invoice and credit note a unique number from a sequence with no gaps and no reuse. Take it in the transaction that issues the invoice.
- How: Article 226 of the EU VAT Directive requires a sequential number that identifies each invoice. Several countries also forbid gaps. A database sequence skips numbers when a transaction rolls back, so use a counter row updated inside the transaction. Separate series, such as one per year or per company, are allowed with a prefix. Never delete an issued invoice.
- Check: A query over each series finds no gaps and no duplicates. A test that issues 50 invoices at the same moment gets 50 different numbers in a row.

Example:

```sql
UPDATE invoice_series SET last_number = last_number + 1
WHERE series = '2026-EB'
RETURNING last_number;
```

```js
async function issueInvoice(db, draft) {
  return db.tx(async t => {
    const { last_number: number } = await t.one('UPDATE invoice_series SET last_number = last_number + 1 WHERE series = $1 RETURNING last_number', [draft.series])
    return t.one('INSERT INTO invoices (series, number, order_id, issued_at) VALUES ($1, $2, $3, now()) RETURNING *', [draft.series, number, draft.orderId])
  })
}
```

```python
def issue_invoice(connection, draft):
    with connection.transaction():
        (number,) = connection.execute(
            "UPDATE invoice_series SET last_number = last_number + 1 WHERE series = %s RETURNING last_number",
            (draft.series,),
        ).fetchone()
        return connection.execute(
            "INSERT INTO invoices (series, number, order_id, issued_at) VALUES (%s, %s, %s, now()) RETURNING *",
            (draft.series, number, draft.order_id),
        ).fetchone()
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers payments playbook: https://agentwranglers.se/playbook/payments/invoices-and-records/sequential-invoice-numbers`.

- Next: [Put every required field on the invoice](https://agentwranglers.se/playbook/payments/invoices-and-records/invoice-required-fields)
- [Invoices and records](https://agentwranglers.se/playbook/payments/invoices-and-records)
