How mdfi scores every page, escalates the ones that need it, and shows what it is unsure about.

## Three tiers

Every page is read once by Tier 1. A page that scores badly can be read again by a stronger tier, which costs more.

| Tier | What reads the page | Weight |
| --- | --- | --- |
| 1 | For PDF, Office, HTML and image files, a layout-aware document parser ([Docling](https://github.com/docling-project/docling)): the text layer of born-digital pages, with OCR for pages and regions that are images. Text and email files are parsed directly. | 1 |
| 2 | Enhanced OCR: for a PDF page, the whole page is OCR'd again at twice the resolution | 2 |
| 3 | A vision model, sent that one page on its own | 5 |

The weight is what a page costs in weighted pages; [Pricing](pricing.md#weighted-pages) explains the charge.

**Only PDF and image pages escalate.** Word, PowerPoint, Excel, HTML, email and text pages are read once, at Tier 1. A low score on one of those pages is flagged, not re-read, and costs nothing extra. Every plan can use all three tiers.

## Page scores

Every page gets a score from 0 to 100. A page that scores **85 or more** is accepted as it is. A PDF or image page under 85 is a candidate for a re-read.

What lowers a score:

- A scanned page (fewer than 50 characters of text layer and an image over half the page; every page of an image file counts as one) whose OCR returned fewer than 30 words scores 20, unless its small text layer came through whole, such as a cover title over a full-page photo.
- A page with a text layer of over 200 characters whose conversion kept less than a tenth of it scores 30.
- OCR that lost the spaces between words (`tohavepaidyourpartofthetax`) caps the page at 60. This is checked on OCR output only, never on a born-digital text layer.
- A table that fails a check a better reader could fix (see [Tables](#tables)) caps the page at 70, under the accept score.
- Broken table markup, garbled or repeated output, and OCR noise on scanned pages take points off.

A blank page (no text and no image) is not penalised: it scores 100.

## Flags and warnings

A page's flags say what mdfi noticed about it. The API lists them by name in each page's `flags`; the connector shows each one with its label:

| Flag | Label | What it means |
| --- | --- | --- |
| `table_uncertain` | uncertain tables | A table on the page failed a check that a better reader could fix. |
| `table_issues` | table problems | The page's table markup is broken: rows of different widths, or mostly empty cells. |
| `table_missed` | a captioned table was read as text (no table id, so it cannot be exported) | The page has a caption such as "Table 10-1." but its table came back as plain text. The text is readable; there is no table to export. |
| `ocr` | read by OCR | The page is a scan. A scan that scores 85 or more is not a concern and is listed once instead, as `read from scans: pages 1–40`. |
| `no_text_recovered` | no text recovered from scan | OCR returned fewer than 30 words from a scanned page. |
| `ocr_noise` | OCR noise | The OCR output of a scanned page has the marks of misreading, such as many stray single characters. |
| `glued_words` | words run together (spacing lost) | OCR lost the spaces between words. |
| `text_missing` | text layer present but little or nothing extracted | The page has a text layer, but its conversion came back empty or nearly empty. With a text layer of over 200 characters the page scores 30 and is re-read; with a smaller one (often only a running header or footer) it is flagged but not re-read. |
| `degenerate` | garbled output | The output degenerated into long runs of blank space or a repeated character. |
| `low_quality` | low quality | The page still scores under 85, after any re-reads. |
| `not_enhanced_cap` | could not be enhanced (escalation cap reached) | A re-read would have gone over the per-document escalation cap or the pages left this month, so the page was kept as it was. |
| `vision_unavailable` | vision reader unavailable | The vision model was not available, or returned nothing, for this page. |
| `not_converted` | page could not be converted | Tier 1 could not convert this page, even on its own, and no re-read replaced it. Tier 1 charges nothing for such a page. |

**A ⚠ means: check this before relying on it.** The connector puts one in three places: under `⚠ uncertain — verify before relying on these:` in the `read_document` header, which lists the uncertain tables, the flagged pages and any warnings; after every outline entry whose pages hold a flagged page or an uncertain table; and on the line under a page's `--- page N ---` marker in `read_pages`. Clean scans and informational table notes never get a ⚠; those notes are shown on plain `table notes:` and `note:` lines instead. For example, `read_pages` with `pages="5"` on the report from the [Quickstart](index.md#what-your-agent-sees) returns:

```text
--- page 5 ---
⚠ table p5-t1: "Total" row 14 does not add up in column "2025"
note: table p5-t1: continues on page 6
## Regional results
[table p5-t1 — 14 rows × 4 cols, uncertain: "Total" row 14 does not add up in column "2025"]
Region	2024	2025	2026
…
```

The API's manifest also carries three neutral flags that the connector does not show: `empty` (a blank page), and `enhanced_tier2` and `enhanced_tier3` (the page's text came from that tier).

## Escalation

After Tier 1, mdfi re-reads the PDF and image pages that scored under 85, by these rules:

1. **Worst first.** Pages are taken in order of score, lowest first.
2. **Tier 2, then Tier 3.** Each page tries enhanced OCR, then the vision model, and stops as soon as it scores 85 or more.
3. **A table-only problem skips Tier 2.** When a page's text layer was read cleanly and only its table is in doubt, re-running OCR would not rebuild the table, so the page goes straight to the vision model. If no vision model is available, it is not re-read (and not charged) and stays flagged.
4. **Within the cap and the allowance.** Each step runs only if its extra cost still fits: at most 100 extra weighted pages per document, and never so much that the document would cost more than the pages left this month. Tier 2 adds 1 to a Tier 1 page, Tier 3 adds 3 after Tier 2, or 4 straight from Tier 1. A page whose next step does not fit is flagged `not_enhanced_cap` (shown by the connector as "could not be enhanced (escalation cap reached)"), and the other pages carry on.
5. **A re-read must be better.** Its text replaces the page only if it scores higher. Unless the page was blank or a scan, it must also keep at least half the page's words, so a short fragment never replaces a full page.
6. **Charged for the highest tier that returned output**, even when a lower tier's version is kept. A tier that fails or returns nothing is not charged.

A scan that Tier 1 reads well (85 or more) is not re-read, so it costs 1. The vision model is sent only the one page it re-reads; [Data and privacy](data.md) covers what leaves mdfi.

## Outline first, then pages

The outline comes as early as the file allows:

- **PDF bookmarks and PowerPoint slide titles** are read from the file before conversion starts, so the agent can pick a section at once. An image shows its frame count, with no outline.
- The file's outline is kept to the end when it has at least 3 entries and none of them covers more than 80% of the pages. Otherwise, and for files with no bookmarks or slide titles, the outline is built from the document's headings once Tier 1 is done.
- **A PDF without bookmarks** gets an outline from the headings of the pages converted so far.
- **Word, Excel, HTML, email and text files** get their outline from their headings once their text is converted, just before the document is ready.

Pages become readable as they are ready:

- **PDF:** Tier 1 reads a PDF a few pages at a time (the first batch is at most 4 pages), and each batch is readable as soon as it is done. A page waiting for a re-read becomes readable when its re-read finishes.
- **Images and PowerPoint:** pages are readable once Tier 1 is done, except image pages waiting for a re-read, which follow one by one.
- **Word, Excel, HTML, email and text:** readable only when the whole document is ready. Their page count is only estimated before conversion, and a document that fails its final check is not charged, so showing its pages early would be a free read.

While a document converts, `read_document` lists the ready pages and `read_pages` returns the ready ones, naming the rest as still converting. The agent asks again later; nothing is charged until the document is ready.

## Time left

While a document converts, the connector may add an estimate, such as `should be ready in about N minutes (estimate)`. It is worked out from the pace of the pages converted so far, counting the pages still to convert and the pages queued for a re-read. It is an estimate, not a promise, and it is left out when there is nothing to base it on: before the first page is converted, when only re-reads remain (the pace of Tier 1 says nothing about how long a re-read takes), and when the conversion has run past its own projection.

## Tables

Each table in the page text starts with a label line, followed by its rows with tab-separated cells:

```text
[table p13-t1 — 42 rows × 6 cols]
```

The id `p13-t1` means page 13, first table. The tables on a page are numbered in reading order, counting one continued from the previous page, and a table that runs over several pages keeps the id of its first page.

mdfi checks every table. A table that fails a check is **uncertain**, and its label says why, naming up to two problems: `[table p13-t1 — 42 rows × 6 cols, uncertain: <problem>; <problem> (+1 more)]`. The problems it reports:

- rows with a different number of cells from the header;
- a header with no rows;
- empty or duplicate header cells;
- text in a column of numbers, or cells that look like misread numbers (`2O` for `20`);
- a cell holding several numbers, often two rows merged into one;
- a header row that looks like data;
- a "Total" row that does not add up;
- `page read with low confidence (score N)`: the table is on a page that still scores under 85;
- `may continue as p14-t1 on page 14 (headers differ)`: a table ends a page and the next page starts with a table under different headers.

The first seven checks also run on each page by itself. A problem they find caps the page's score at 70, and on a PDF or image page that sends the page through [escalation](#escalation). Three problems do not cap the score, because a stronger reader would not change them: a total that does not add up (the source itself may not), a table that is only a header at the end of a page (its rows may be on the next page), and a header that looks like data on a table that starts its page (a continuation whose header was not repeated). Those tables stay marked uncertain.

Some notes are informational and never make a table uncertain: `read by vision — verify` (every table the vision model read), `two-level header combined`, `continues on page N`, and, while the document converts, `may continue on page N (still converting)`.

`export_table` writes one table to a CSV file and, when the table has notes, a notes file beside it. It works once the document is ready, because a page still being re-read can extend a table. The [Connector](connector.md) page has the details.

## One conversion per file

The connector identifies a file by its SHA-256. It sends the hash first; if your account already has that document, mdfi returns it and charges nothing. If the file is still converting, the same document comes back with its progress. Only a file mdfi has not seen for your account is uploaded and converted. Any change to the file gives a new hash, so an edited file is a new document. Documents are kept per account: another account's copy of the same file is converted separately for that account.

A converted document is kept for 7 days. After that, reading the file again converts and charges it again. A conversion that failed keeps no pages, so trying again converts the file again.
