> ## Documentation Index
> Fetch the complete documentation index at: https://docs.corvic.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Document Lens

> Teach the room to read one kind of document. Approve the fields once, and every document — today's and next year's — comes back as structured data.

A **Document Lens** is agentic document processing: teach it once what to pull out of one kind of document — invoices, applications, drawings — and every document comes back as structured data your stack can query. You show it a few representative documents, review the layout and fields it finds, approve them once, and then it just runs.

The approved configuration becomes a frozen, versioned **contract**. Two callers naming the same lens and the same document get the same answer, and next quarter reads like this quarter.

<Note>
  Document Lens is currently in **beta**. It lives inside a [Room](/features/rooms) — open the **Lens** surface from the room's surface switcher, next to Chat, Playbooks, and Memory.
</Note>

## Why a lens

<CardGroup cols={3}>
  <Card title="Reads complex documents" icon="eye">
    Dense tables, handwriting, stamps, and any language — it looks at the page, not just the text layer underneath.
  </Card>

  <Card title="Costs a fraction to run" icon="coins">
    Agreed once — ten thousand documents become ten thousand bounded model calls, not ten thousand open-ended conversations.
  </Card>

  <Card title="Built for your stack" icon="code">
    One API call, one token, and a table of results at an address that does not move.
  </Card>
</CardGroup>

And on the hard ones — scanned, annotated, badly photographed, inconsistent, not all in one language:

* **It checks its own work.** Say the line items must add up; every reading is tested first.
* **It says when it is unsure.** When the page disagrees with itself you get told, not a quiet guess.
* **It reads any language.** Dates and numerals are normalised, whatever script they arrive in.
* **Versioned, not silently changed.** Edit the schema and the version bumps. Old readings still hold.

## Building a lens

<Steps>
  <Step title="Show it a few documents">
    Click **Build a lens** on the Lens surface. Pick the lens purpose — **Extract fields**, **Classify documents**, or **Extract images** — describe **what these documents are**, and point it at the data source holding a few representative examples (two or three of the messy ones). You can optionally list the fields to read up front, and choose whether this lens will read **a few documents at a time** or **a corpus, repeatedly**.
  </Step>

  <Step title="Approve the fields">
    The lens reads your samples and proposes the layout it found and the fields worth extracting. In the lens workbench, review the proposal on the **Contract** tab — field names, types, which are **required**, and the checks that must hold. Fix what it got wrong, then approve the configuration as **the contract every reading uses**.
  </Step>

  <Step title="Point the pile at it">
    It reads the rest, and everything that arrives after them, without being asked again. Use the **Evaluate** tab to read documents and inspect results, and the **Runs** tab to follow batch runs.
  </Step>
</Steps>

<Tip>
  **You approve a lens once, then it just runs.** Changing the schema afterwards bumps the contract's version rather than silently changing it — readings made under the old version still hold.
</Tip>

### Reading modes

A lens serves its readings in one of two modes, chosen on the lens's configuration pane:

* **Auto** — approve a schema for your documents, then read them all the same way. This is the default and right for most lenses.
* **Playbook** — bind a [Playbook](/features/playbooks) that serves the lens, for readings that need a custom multi-stage process behind the same contract.

A lens shows its readiness at a glance — **Not built**, **Needs approval**, **Needs a parse**, **Not serving**, or **Ready**. A lens still being agreed is listed but cannot be called yet.

## Two ways to run one agreement

You pick the serving per call, not per lens — read the hard one properly while somebody waits, and read the other ten thousand overnight, off the same agreement, at the same version, producing the same fields.

|                  | Read one now                                                                                                                           | Read the pile                                                                                           |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| **Scale**        | A document at a time                                                                                                                   | Ten thousand documents                                                                                  |
| **How it reads** | An agent reads the pages properly — catches contradictions, reconciles handwriting against the printed line, and tells you what it did | The same agreed fields, run over the whole corpus on a schedule, as one bounded model call per document |
| **Reports**      | Repairs, contradictions, and missing values                                                                                            | Checks run in SQL over the extracted columns; failures land as rows you can query                       |
| **Best for**     | The hard ones, and anything a person is waiting on                                                                                     | Whole corpora, on a schedule — only what is new is read on each run                                     |
| **Runs via**     | The Evaluate tab, your app, your agents, or MCP                                                                                        | The lens's batch [Workflow](/features/workflows)                                                        |

## What a reading returns

Every reading answers with the same structure:

| Field                | What it holds                                                                                                                       |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `fields`             | Exactly the agreed schema — every field, at its agreed nesting, `null` where the document did not say                               |
| `reasoning`          | The account of the reading: what could not be found, which checks failed, which corrections and contradictions the document carries |
| `repairs`            | What the reading corrected                                                                                                          |
| `missing_required`   | The agreed-required fields that came back empty                                                                                     |
| `extraction_version` | Which version of the contract answered                                                                                              |

<Warning>
  If `missing_required` names anything, the lens could not read this document — report that rather than treating a partial reading as a complete one. The fields that did come back look exactly like a good answer.
</Warning>

## Calling a lens from your stack

You agree the fields in the product, in about the time it takes to check them by hand once. After that it is an API your code owns, reachable over [MCP](/integrations/mcp-overview):

| Tool                                  | What it does                                                                                                                                                   |
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `list_lenses`                         | List a room's lenses: what kind of document each reads, the fields it returns, and whether it is ready to call.                                                |
| `extract_document`                    | Read one document through a lens and get the full reading back in the call.                                                                                    |
| `start_extraction` / `get_extraction` | The same reading, split for long or scanned documents: start it, then poll for the result. The reading survives a client that walks away, and polling is free. |

A few facts that make integration easy:

* One bearer token authorises the upload, the run, and the read.
* Readings land in one table at an address that does not change.
* Fields come back as JSON, so a schema change bumps a version instead of breaking your reader.
* Failures are rows too — you can query which of last night's documents did not come back.

<Note>
  A lens serves **one document at a time**. For a corpus, run the lens's batch workflow and read the table it writes — a tool call per document over a large pile is slow, expensive, and not what the batch serving is for.
</Note>

## Where a lens sits

A lens turns documents into columns; what happens to those columns is the rest of the platform. Accumulate what your lenses read into a [Virtual Memory](/features/virtual-memory) you can query, keep the extracted tables current with a scheduled [Workflow](/features/workflows), and give your own [agents](/features/agents) `extract_document` as a tool they can simply call.

## Related Documentation

<CardGroup cols={2}>
  <Card title="Virtual Memory" icon="brain" href="/features/virtual-memory">
    Accumulate what your lenses read into a graph or a space you can query.
  </Card>

  <Card title="Workflows" icon="sitemap" href="/features/workflows">
    The batch serving behind a lens, keeping the extracted tables current.
  </Card>

  <Card title="Document Digitization" icon="file-lines" href="/features/document-digitization">
    The parsing actions available on the workflow canvas.
  </Card>

  <Card title="Playbooks" icon="list-check" href="/features/playbooks">
    Bind a playbook to serve a lens with a custom process.
  </Card>

  <Card title="Agents" icon="robot" href="/features/agents">
    Give agents lenses as tools they can call.
  </Card>

  <Card title="MCP Overview" icon="plug" href="/integrations/mcp-overview">
    Call lenses from your own applications.
  </Card>
</CardGroup>
