Skip to content
Blog

Splitting multi-invoice PDFs by invoice-number label

Suppliers send one file with twenty invoices and a cover sheet. Here is the rule that turns it into twenty documents, and where it stops.

The test pack we use internally is a 21-page PDF: a summary page followed by invoices 1–20 from ten suppliers in three countries, some in English and some in German. Uploading it produces 20 processed documents, all fully matched against the matching ERP export, and no document for the summary page. This article is about the small, deliberately dumb rule that makes that happen.

By Deepak Rao Gaikwad, LedgerSync · 23 October 2026

The rule: a label, then a value

Before extraction, the text of every page is read and tested for an invoice-number label followed by a value. The labels come from the same lexicon packs the extractor uses — Invoice No., Invoice Number, Invoice #, Bill No., Document No., and from the German pack RECHNUNG, Rechnungsnummer, Belegnummer, Beleg-Nr.. The value may be separated by a colon, dot, hash or dash and must be at least three characters of letters, digits, slashes or dashes. In code:

src/modules/documents/multi-invoice-split.ts
export function pageLooksLikeInvoiceStart(text: string, labels: readonly string[]): boolean {
  if (!text.trim()) return false
  for (const label of labels) {
    const re = new RegExp(`${escapeRegex(label)}\\s*[:.#\\-–]?\\s*([A-Za-z0-9][A-Za-z0-9\\/\\-]{2,})`, 'i')
    if (re.test(text)) return true
  }
  return false
}

A page that passes starts a new invoice. A page that fails belongs to the invoice before it. Pages before the first start — the cover sheet, a suite directory, a summary — are dropped. The split only happens when at least two start pages are found; a PDF with one invoice is ingested whole.

A five-page example, with the output

We generated a five-page PDF with one line of text per page and ran the splitter on it. The page texts were:

Pages of the generated example PDF
PageText on the pageStarts an invoice?
1Suite Directory TEST INVOICE 1: Stellar DenimNo — “INVOICE 1” is a title, not a label with a value
2Invoice No. 9000120481Yes
3RECHNUNG 2/984712/3349120Yes
4Seite 2 von 2No — attaches to page 3
5Belegnummer SF-551249Yes
invoiceStartPages(pageTexts, labels) → 0-based indexes, then splitMultiInvoicePdf(buffer, 'pack.pdf')
[ 1, 2, 4 ]
pack-01.pdf 1 page(s)
pack-02.pdf 2 page(s)
pack-03.pdf 1 page(s)

Three documents: the English invoice, the two-page German invoice (Seite 2 von 2 has no label, so it stays with the RECHNUNG page), and the Belegnummer invoice. The summary page produced nothing. Each part keeps the original pages intact — it is rebuilt with pdf-lib from the source pages, not re-rendered — so extraction sees exactly what the supplier printed.

Why the rule is not smarter

  • Titles are not labels. “TEST INVOICE 1” on a directory page is a heading, not a field; it has no value after a label, so it is ignored. That one distinction removes most false starts in packs that begin with an index.
  • Continuation pages take care of themselves. A second page of line items has no invoice-number field, so it attaches to the invoice before it. Nothing has to detect “page 2 of 2”.
  • Languages are a lexicon concern, not a splitter concern. Adding a Danish or French label is one line in a pack file; the splitter never changes.

Fallbacks and limits

  • If the PDF has no readable text (a pure image scan), or the split throws for any reason, the original file is ingested unchanged — it is never dropped.
  • Each part becomes its own document and counts toward the batch limit (40 files per upload by default). A pack that splits into more parts than that reports the extras as ignored rather than failing the upload.
  • Files arriving through the SFTP inbound connector are not split; each file there is treated as one document.
  • A page that carries two invoice-number labels (a credit note referencing the original invoice, for example) still starts exactly one document — the split is per page, never within a page.

If a split looks wrong

Open the original file from the document and check which pages carry a label with a value. A missing split almost always means the label on that page is not in the lexicon yet; an extra split means a non-invoice page printed something like Document No. 12345. Both are one-line fixes on our side — send us the page.

Ready to see it on your own invoices?