Document processing

Turn PDFs and scanned documents into typed rows for your production systems.

Operations teams use document processing agents to move information from invoices, contracts, forms, and scans into databases, ERPs, and review queues. Agno can parse model output from files and images into Pydantic objects. Workflows add approvals, batch execution, retries, and schedules.

Define a Pydantic schema and pass the document through File. Check that the run completed and returned an instance of that schema.

output_schema requests structured output and Agno attempts to parse it. A failed or unparseable run can leave content as text. Check RunStatus.completed and the expected Pydantic type before reading fields, indexing, or writing downstream. These examples stop on failure; an application can instead send the original input to a review queue. Schema validation checks the shape and constraints, so verify extracted facts against the source separately.

Before running these examples, create and activate a Python environment, then install the provider and set its key:

uv venv .venv --python 3.12
source .venv/bin/activate
uv pip install "agno[openai]"
export OPENAI_API_KEY="..."

Replace every https://example.com/... PDF URL with a real PDF URL accessible to the model provider, or use File(filepath="...") for a local PDF. Create the referenced local files before running the example. Extraction quality depends on the document and model; the output comments are illustrative.

from agno.run.base import RunStatus

from typing import List, Optional

from agno.agent import Agent
from agno.media import File
from agno.models.openai import OpenAIResponses
from pydantic import BaseModel, Field


class LineItem(BaseModel):
    description: str
    quantity: Optional[float] = None
    unit_price: Optional[float] = None
    amount: Optional[float] = None


class Invoice(BaseModel):
    invoice_number: Optional[str] = Field(None, description="As printed on the invoice")
    vendor: Optional[str] = None
    invoice_date: Optional[str] = None
    due_date: Optional[str] = None
    subtotal: Optional[float] = None
    tax: Optional[float] = None
    total: Optional[float] = None
    currency: Optional[str] = Field(None, description="ISO 4217, e.g. USD, EUR")
    lines: List[LineItem] = Field(default_factory=list)


agent = Agent(
    model=OpenAIResponses(id="gpt-5.5"),
    instructions=(
        "Extract invoice fields and line items from the attached PDF. "
        "Use exactly what the document shows. If a field is missing, "
        "leave it null. Do not guess."
    ),
    output_schema=Invoice,
)

result_run = agent.run(
    "Extract the invoice.",
    files=[File(url="https://example.com/invoice-1042.pdf")],
)
if result_run.status != RunStatus.completed or not isinstance(result_run.content, Invoice):
    raise RuntimeError("No validated Invoice; send the input to review before continuing")
result = result_run.content
# Invoice(invoice_number='1042', vendor='Acme Corp', invoice_date='2026-04-12',
#         due_date='2026-05-12', subtotal=1200.0, tax=96.0, total=1296.0,
#         currency='USD', lines=[LineItem(...), LineItem(...)])

After the checks above, result is an Invoice. Apply business validation, such as reconciling totals and checking the vendor, before an INSERT, ERP call, or queue message.

Workloads

WorkloadPage
Invoices, receipts, statementsInvoices and receipts
Contracts, MSAs, policiesContracts
Resumes, applications, KYC intakeForms and intake

Production concerns

You need toPage
Process a folder or a queue of documentsBatch and durability
Schedule a nightly run that retries on failureBatch and durability
Route low-confidence fields to a humanHuman routing and eval
Track accuracy against a labeled golden setHuman routing and eval

Explore

Developer Resources