Quickstart — from PDF to proof
Ten minutes from install to a claim a human can check. The point is not "extract text" — it is never have to guess a page number again.
New to the problem?
Read Stop PDF hallucinations first — it explains why a text dump makes agents wrong in a way that sounds confident.
1. Install
npx -y @sylphx/citraAdd it to your host as shown in Installation. Verify with npx -y @sylphx/citra --help.
2. Your first read
One call. Let Citra choose the extraction route:
{
"sources": [{ "path": "/absolute/path/to/report.pdf" }]
}That is the whole request. With no include_* flags, read_pdf profiles the document, picks high-value extraction options, and returns the Agent Document Twin: text, tables, structure, and citations in one response.
Deepen it without learning every switch:
{
"sources": [{ "path": "/absolute/path/to/report.pdf" }],
"auto_detail": "full"
}3. Read the evidence, not the prose
A real response (excerpt) against a two-page table fixture:
{
"route": { "engine": "rust-core", "path": "rust-read-pdf-v1" },
"source": { "hash": "99d313eb…", "path": "…/selectable-table-v1.pdf" },
"results": [{
"data": {
"table_info": [{
"page": 1,
"bounding_box": { "left": 72, "top": 151, "right": 454.8, "bottom": 79 },
"colCount": 3,
"cellCount": 9,
"confidence": 0.92,
"continuation": {
"role": "starts",
"groupId": "table-continuation-p1-table-1-p2-table-1",
"signals": ["same_column_count", "repeated_header_candidate"]
}
}]
}
}],
"gaps": []
}Three things worth noticing:
page+bounding_box— the claim has a place in the document.continuation— the table continues onto page 2 with matching columns. An agent that cites "the table" now knows to read the next page too.gaps— when Citra cannot prove something, it names the gap instead of filling it. That is the whole difference.
4. Search first, read second
Reading everything is slow and expensive. Locate first:
{
"sources": [{ "path": "/absolute/path/to/report.pdf" }],
"query": "revenue"
}search_pdf returns page numbers, snippets, offsets and bounding-box provenance — enough to decide whether to spend tokens on a deep read.
5. Verify before you claim
When an agent is about to assert a number, send it to the evidence tool:
{
"operation": "extract_regions",
"sources": [{ "path": "/absolute/path/to/report.pdf" }],
"regions": [{ "page": 1, "bounding_box": { "left": 72, "top": 151, "right": 454, "bottom": 79 } }]
}Or render the page and look at it:
{
"operation": "render_page",
"sources": [{ "path": "/absolute/path/to/report.pdf" }],
"pages": [1]
}pdf_evidence has five focused operations — inspect, render_page, extract_regions, ocr_pages, analyze_regions. One tool, one op enum; no near-duplicate vanity tools to learn.
6. Scanned or mixed documents
{
"sources": [{ "path": "/absolute/path/to/scanned.pdf" }],
"pages": [1, 2, 3],
"include_ocr_text_layer": true,
"include_tables": true
}OCR keeps its own provenance and confidence and is kept separate from selectable text. OCR word boxes can feed table extraction, so a scanned table still comes back with cells and geometry. Configure a provider per Installation — core reading never needs one.
7. Trust signals — only when you ask
Hidden text, prompt-injection attempts, overlapping or spoofed content:
{
"sources": [{ "path": "/absolute/path/to/untrusted.pdf" }],
"include_safety_findings": true,
"include_trust_report": true
}These are off by default: you pay for them when you need them.
A prompt you can paste into your agent
Read
/absolute/path/to/report.pdfwith Citra. Then answer my question and cite the page and — for numbers — the table and cell you took them from. If the document does not prove an answer, say so instead of guessing.
That last sentence is what makes the evidence path do its job.
Where next
- API reference — every option and result field
- The evidence contract — what "proof" means here
- Performance — how fast, and how that was measured
- Comparison — why not the alternatives