Three things carry the evidence, and none of them is the pile. The generator and the template it renders. The input variants that change the shape of a page. And a sample of real output, checked against both. Do those and you can say something true about how files come out of the system. Skip to the pile and you are counting sand, because the population grows every billing cycle and the count you finished last month is already wrong.
That is the honest shape of this work, and it is worth being blunt about what it buys you. A checked sample tells you the template was right for the inputs you tested on the day you tested them. It says nothing about a file the system has not produced yet. Anybody promising otherwise is selling a certificate that no evidence supports, and the way to get value out of the review is to accept that limit and build a retest trigger around it instead.
Why the Usual Document Audit Does Not Fit
A normal document set is finite. Four hundred PDFs in an uploads folder is a big job and it is a job with an end, which is why the scoping questions for a document audit start with an inventory. A statement run has no inventory to take. Every customer gets a file, every cycle, and each one is a new document that nobody has ever opened.
So the unit of work moves upstream. The interesting question stops being is this file tagged and becomes does this system tag, which is a question about code and templates rather than about documents. That reframing is the whole page, and it changes who has to be in the room. A document vendor cannot answer it. The team that owns the rendering library can.
The Three Layers That Actually Get Tested
Keep these separate in the report, because they fail differently, they are repaired by different people, and a finding filed against the wrong layer gets fixed in the wrong place.
| Layer | What the evidence looks like | What it can and cannot settle |
|---|---|---|
| The generator | The library or service and its version, the export settings, whether tagging is switched on at all, and whether the output carries a language and a title | Settles whether accessible output is even possible. A generator that emits untagged files makes every other question moot |
| The template | The layout the generator fills in, with its headings, its table headers, its logo, its footer artifacts and its field placement | Settles the structure of a correct file. It cannot settle what happens when real data pushes that structure around |
| The output sample | Real files, named and dated, chosen to cover the input variants rather than chosen at random from last month | Settles that the first two held for the cases you tested. It certifies nothing about untested cases or future releases |
Pick the Input Variants Before You Pick the Files
This is the step teams skip, and skipping it is why a sample of 20 statements can be 20 copies of the same test. The variants that matter are the ones that change document structure rather than the ones that change the numbers. A different total is not a different document. A table that runs onto a second page is.
- A table that breaks across a page. Does the header row repeat and stay tagged as a header on the second page, or does the continuation read as a table with no headings at all?
- An empty optional block. A statement with no adjustments, no discount line, no message from the billing team. Templates that leave an empty tagged container behind produce a heading with nothing under it.
- A value long enough to wrap. A company name across two lines, an address with five, a product description that pushes the next heading onto a new page.
- A zero, a credit and a negative. These usually change which blocks render, and a block that only appears on refunds is a block nobody ever checked.
- A different language or currency. A file whose language entry says English while the content is German fails a rule that has nothing to do with tagging, and it fails silently.
- An appended page from somewhere else. Terms, a regulatory notice, a marketing insert. It came from a different template and it often arrives untagged, and it lands on the end of an otherwise clean file.
Six variants across three document types is 18 files, and that is a sample somebody can actually read. Choosing them this way also gives the report a spine. Each finding names the variant that produced it, so the fix is a template change with a test case attached rather than a note about one customer's invoice.
What to Check Inside One Generated File
The checks are the ordinary ones, and W3C has published the shape of them for PDF specifically. The techniques that bear on machine output are the structural ones. Tags on the content, a reading order that follows the page, real table markup with header cells, headings marked as headings, a document title and a language set in the file, images either described or marked as artifacts, and real form fields where the file expects input.
Two of those are nearly free and both get missed by generators. Setting the document title in the file takes two steps rather than one, because the title has to be there and the file has to be told to show it instead of the filename. And setting the document language is a single entry that decides which voice reads your statement aloud. Both are template settings, so fixing them once fixes every file the system will ever produce.
Skip the advice about running text recognition, which is the first thing most PDF guidance reaches for. That is for scans. A generated file is born digital and already has real text in it, so the failure mode is different. The words are there, and nothing has told anybody what they are.
Where Generated Files Break and Handwritten Ones Do Not
The pattern is always the same, and once you have seen it you spot it everywhere. The template is fine and the data is not. Somebody built a beautiful invoice, tagged it properly, tested it with a short customer name and three line items, and shipped it. Then reality arrives with forty line items, a company name that runs to two lines, and a credit note.
So the reading order wanders on the awkward statements and holds everywhere else. The table header disappears only on statements long enough to break. The heading structure collapses only when an optional block is missing. None of these is visible in the file the developer tested, and all of them are visible in the files your customers get. That is the argument for testing output rather than trusting the template, and it is also the argument for keeping the sample small enough that a person really reads it.
The Retest Trigger Is a Release, Not a Date
An annual document audit makes no sense for something that is regenerated nightly. What makes sense is tying the retest to the thing that can actually break it, which is a change to the template, the rendering library or the shape of the data going in. Write those three triggers into the release process and the review keeps its value between engagements.
There is a cheap automated layer here too, and it belongs in the pipeline rather than in a report. A structural check can run against a generated sample on every release, the way any other test does, and it catches the mechanical regressions for the cost of setting it up once. It reaches exactly as far as any other scanner, which by our own count is 10 of the 356 WCAG techniques and documented failures we graded, so treat it as a tripwire rather than an answer. The same reasoning, applied to a website, is in accessibility checks in CI.
One honest limit
We audit and we never repair, so a pipeline review hands your team the template changes and the test cases rather than a quote for making them. A generated-document engagement is also not a page-count job, so it gets scoped and quoted through contact rather than ordered off the shelf. If what you have is a fixed set of finished files instead, that is the ordinary PDF accessibility audit.