Agent example: document intake
The job: contracts, agreements and letters arrive as PDFs, some digital and some scanned. The agent reads each one, pulls out the key facts, flags risks, and writes a one-page review note in Word for the legal or purchase team.
Tools used: POST /pdf for exact text, POST /ocr for scans, structured JSON on
/v1/chat/completions, and file creation on POST /generate.
The code
Section titled “The code”import base64, json, os, timefrom pathlib import Pathimport requestsfrom openai import OpenAIfrom pydantic import BaseModel
BASE = "https://dummydomain"HEADERS = {"Authorization": "Bearer " + os.environ["API_KEY"]}client = OpenAI(base_url=BASE + "/v1", api_key=os.environ["API_KEY"])
def attach(path: Path) -> list[dict]: return [{"name": path.name, "data": base64.b64encode(path.read_bytes()).decode()}]
def ocr(path: Path, deadline_s: int = 600) -> str: r = requests.post(f"{BASE}/ocr", headers=HEADERS, timeout=60, json={"attachments": attach(path), "pages": "all", "view": False}) r.raise_for_status() job_id, give_up = r.json()["job_id"], time.time() + deadline_s while time.time() < give_up: job = requests.get(f"{BASE}/ocr/jobs/{job_id}", headers=HEADERS, timeout=30) job.raise_for_status() job = job.json() if job.get("status") == "done": return job["result"]["text"] if job.get("status") in ("failed", "error", "cancelled"): raise RuntimeError(f"OCR job {job_id} ended as {job['status']}") time.sleep(5) raise TimeoutError(f"OCR job {job_id} did not finish in {deadline_s} seconds")
def read_text(path: Path) -> str: r = requests.post(f"{BASE}/pdf", headers=HEADERS, timeout=120, json={"attachments": attach(path), "mode": "text", "pages": "all"}) r.raise_for_status() text = r.json()["files"][0]["text"] return text if len(text.strip()) > 200 else ocr(path) # little text means a scan
class Review(BaseModel): document_type: str parties: list[str] key_dates: list[str] amounts_inr: list[str] summary: str risks: list[str]
def review(text: str) -> Review: reply = client.chat.completions.create( model="Smart", messages=[{"role": "system", "content": "You review business documents for an Indian company. " "Use only the document text. Dates as DD-MM-YYYY. List a risk only if the text supports it."}, {"role": "user", "content": "Document text:\n\n" + text}], response_format={"type": "json_schema", "json_schema": {"name": "Review", "schema": Review.model_json_schema(), "strict": True}}, ) return Review.model_validate_json(reply.choices[0].message.content)
def write_note(name: str, r: Review) -> Path | None: prompt = (f"Create a Word document: a one-page review note for '{name}'. Use these facts only, " "with headings for Summary, Parties, Key dates, Amounts and Risks:\n" + r.model_dump_json(indent=2)) made = requests.post(f"{BASE}/generate", headers=HEADERS, timeout=600, json={"model": "Smart", "prompt": prompt, "stream": False}) made.raise_for_status() art = made.json().get("office_artifact") if not art: return None # no file was made; keep the JSON and tell a person file = requests.get(f"{BASE}/office/artifacts/{art['artifact_id']}/download", headers=HEADERS, timeout=120) file.raise_for_status() out = Path(art["filename"]) out.write_bytes(file.content) return out
pdf = Path("agreement.pdf")result = review(read_text(pdf))print(json.dumps(result.model_dump(), indent=2, ensure_ascii=False))print("Review note:", write_note(pdf.name, result) or "not created")See it run
Section titled “See it run”A real run of the code above, on made-up sample files. Press Replay to watch the steps in order.
What went in
What the agent did
POST /pdf0.1 sRead the text inside the PDF, with no model: 750 characters found.
Request and response
Request { "attachments": [ { "name": "agreement.pdf", "data": "<file, 1,656 bytes>" } ], "mode": "text", "pages": "all" }Response 200 { "files": [ { "chars": 750, "name": "agreement.pdf", "page_texts": [ "SERVICE AGREEMENT\nThis agreement is made on 01-10-2026 between Sharma Traders, Chandni Chowk, Delhi\n(the Client) and Gupta Electricals, Lajpat Rai Market, Delhi (the Contractor).\n1. Scope: the Contractor will maintain all electrical fittings at the Client’s warehouse.\n2. Fee: Rs 2,40,000 per year, paid quarterly in advance within 7 days of invoice.\n3. Term: 12 months from 01-10-2026. The agreement renews automatically for another\n12 months unless either party gives notice.\n4. Penalty: a late visit costs the Contractor Rs 5,000 per day, with no upper limit.\n5. Termination: either party may end this agreement with 90 days written notice.\n6. Jurisdiction: courts at Delhi only.\nSigned: R. Sharma ..." ], "pages": 1, "pages_processed": 1, "text": "SERVICE AGREEMENT\nThis agreement is made on 01-10-2026 between Sharma Traders, Chandni Chowk, Delhi\n(the Client) and Gupta Electricals, Lajpat Rai Market, Delhi (the Contractor).\n1. Scope: the Contractor will maintain all electrical fittings at the Client’s warehouse.\n2. Fee: Rs 2,40,000 per year, paid quarterly in advance within 7 days of invoice.\n3. Term: 12 months from 01-10-2026. The agreement renews automatically for another\n12 months unless either party gives notice.\n4. Penalty: a late visit costs the Contractor Rs 5,000 per day, with no upper limit.\n5. Termination: either party may end this agreement with 90 days written notice.\n6. Jurisdiction: courts at Delhi only.\nSigned: R. Sharma ...", "truncated": false } ], "mode": "text", "ms": 10, "ok": true }POST /v1/chat/completionsSmart1.3 sSmart returned JSON that matches the Review schema.
Request and response
Request { "messages": [ { "role": "system", "content": "You review business documents for an Indian company. Use only the document text. Dates as DD-MM-YYYY. List a risk only if the text supports it." }, { "role": "user", "content": "Document text:\n\nSERVICE AGREEMENT\nThis agreement is made on 01-10-2026 between Sharma Traders, Chandni Chowk, Delhi\n(the Client) and Gupta Electricals, Lajpat Rai Market, Delhi (the Contractor).\n1. Scope: the Contractor will maintain all electrical fittings at the Client’s warehouse.\n2. Fee: Rs 2,40,000 per year, paid quarterly in advance within 7 days of invoice.\n3. Term: 12 months from 01-10-2026. The agreement renews automatically for another\n12 months unless either party gives notice.\n4. Penalty: a late visit costs the Contractor Rs 5,000 per day, with no upper limit.\n5. Termination: either party may end this agreement with 90 days written notice.\n6. Jurisdiction: courts at Delhi only.\nS ..." } ], "response_format": { "type": "json_schema", "json_schema": { "name": "Review", "schema": { "properties": { "document_type": { "title": "Document Type", "type": "string" }, "parties": { "items": { "type": "string" }, "title": "Parties", "type": "array" }, "key_dates": { "items": { "type": "string" }, "title": "Key Dates", "type": "array" }, "amounts_inr": { "items": { "type": "string" }, "title": "Amounts Inr", "type": "array" }, "summary": { "title": "Summary", "type": "string" }, "risks": { "items": { "type": "string" }, "title": "Risks", "type": "array" } }, "required": [ "document_type", "parties", "key_dates", "amounts_inr", "summary", "risks" ], "title": "Review", "type": "object" }, "strict": true } } }Response 200 { "choices": [ { "finish_reason": "stop", "index": 0, "message": { "content": "{\"document_type\":\"SERVICE AGREEMENT\",\"parties\":[\"Sharma Traders\",\"Gupta Electricals\"],\"key_dates\":[\"01-10-2026\"],\"amounts_inr\":[\"Rs 2,40,000 per year\",\"Rs 5,000 per day\"],\"summary\":\"Service agreement for electrical maintenance at the Client's warehouse for a term of 12 months starting 01-10-2026, with automatic renewal and a 90-day termination notice period.\",\"risks\":[\"Late visits cost the Contractor Rs 5,000 per day with no upper limit.\"]}", "role": "assistant" } } ], "object": "chat.completion", "usage": { "completion_tokens": 135, "prompt_tokens": 440, "total_tokens": 575 } }POST /generateSmart8.1 sMade a Word file: Service Agreement Review Note Sharma Traders Gupta Electricals.docx.
Request and response
Request { "prompt": "Create a Word document: a one-page review note for 'agreement.pdf'. Use these facts only, with headings for Summary, Parties, Key dates, Amounts and Risks:\n{\n \"document_type\": \"SERVICE AGREEMENT\",\n \"parties\": [\n \"Sharma Traders\",\n \"Gupta Electricals\"\n ],\n \"key_dates\": [\n \"01-10-2026\"\n ],\n \"amounts_inr\": [\n \"Rs 2,40,000 per year\",\n \"Rs 5,000 per day\"\n ],\n \"summary\": \"Service agreement for electrical maintenance at the Client's warehouse for a term of 12 months starting 01-10-2026, with automatic renewal and a 90-day termination notice period.\",\n \"risks\": [\n \"Late visits cost the Contractor Rs 5,000 per day with no upper limit.\"\n ]\n}", "stream": false }Response 200 { "duration_ms": 7322, "office_artifact": { "artifact_id": "FrLReGO1qz6Zj_kMbJWjt2QSn0XIYUjD", "available": true, "created_at": "2026-10-07T00:29:41.853426Z", "expires_at": "2026-10-14T00:29:41.853426Z", "filename": "Service Agreement Review Note Sharma Traders Gupta Electricals.docx", "kind": "docx_report", "logical_pages": 1, "mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "page_count_mode": "logical", "size": 39069, "title": "Service Agreement Review Note: Sharma Traders & Gupta Electricals" }, "response": "Created Service Agreement Review Note Sharma Traders Gupta Electricals.docx." }GET /office/artifacts/{id}/download0.1 sDownloaded the file.
Request and response
Request (empty)
Response 200 <application/vnd.openxmlformats-officedocument.wordprocessingml.document, 39,069 bytes>
What the program printed
{
"document_type": "SERVICE AGREEMENT",
"parties": [
"Sharma Traders",
"Gupta Electricals"
],
"key_dates": [
"01-10-2026"
],
"amounts_inr": [
"Rs 2,40,000 per year",
"Rs 5,000 per day"
],
"summary": "Service agreement for electrical maintenance at the Client's warehouse for a term of 12 months starting 01-10-2026, with automatic renewal and a 90-day termination notice period.",
"risks": [
"Late visits cost the Contractor Rs 5,000 per day with no upper limit."
]
}
Review note: Service Agreement Review Note Sharma Traders Gupta Electricals.docxFile made: Service Agreement Review Note Sharma Traders Gupta Electricals.docx (38 KB)
What the agent did
POST /pdf0.2 sRead the text inside the PDF, with no model: 0 characters found.
Request and response
Request { "attachments": [ { "name": "agreement-scan.pdf", "data": "<file, 118,500 bytes>" } ], "mode": "text", "pages": "all" }Response 200 { "files": [ { "chars": 0, "name": "agreement-scan.pdf", "page_texts": [ "" ], "pages": 1, "pages_processed": 1, "text": "", "truncated": false, "warnings": [ "scanned_or_empty: little or no text (no OCR)" ] } ], "mode": "text", "ms": 1, "ok": true }POST /ocr0.1 sLittle text inside, so it is a scan. Started an OCR job.
Request and response
Request { "attachments": [ { "name": "agreement-scan.pdf", "data": "<file, 118,500 bytes>" } ], "pages": "all", "view": false }Response 200 { "job_id": "1388eb36c21b4300be39443b6ca0a8ca", "status": "queued" }GET /ocr/jobs/{id}0.1 sChecked the OCR job: running.
Request and response
Request (empty)
Response 200 { "job_id": "1388eb36c21b4300be39443b6ca0a8ca", "name": "agreement-scan.pdf", "progress": { "page": 0, "total": 0 }, "status": "running" }GET /ocr/jobs/{id}0.1 sChecked the OCR job: done, text recognised.
Request and response
Request (empty)
Response 200 { "job_id": "1388eb36c21b4300be39443b6ca0a8ca", "ms": 1986, "name": "agreement-scan.pdf", "ok": true, "progress": { "page": 1, "total": 1 }, "queue_ms": 0, "result": { "chars": 762, "dpi": 120, "name": "agreement-scan.pdf", "page_texts": [ "## SERVICE AGREEMENT\n\nThis agreement is made on 01-10-2026 between Sharma Traders, Chandni Chowk, Delhi (the Client) and Gupta Electricals, Lajpat Rai Market, Delhi (the Contractor).\n\n1. Scope: the Contractor will maintain all electrical fittings at the Client's warehouse.\n\n2. Fee: Rs 2,40,000 per year, paid quarterly in advance within 7 days of invoice.\n\n3. Term: 12 months from 01-10-2026. The agreement renews automatically for another 12 months unless either party gives notice.\n\n4. Penalty: a late visit costs the Contractor Rs 5,000 per day, with no upper limit.\n\n5. Termination: either party may end this agreement with 90 days written notice.\n\n6. Jurisdiction: courts at Delhi only.\n\nSigned ..." ], "pages": 1, "pages_processed": 1, "text": "## SERVICE AGREEMENT\n\nThis agreement is made on 01-10-2026 between Sharma Traders, Chandni Chowk, Delhi (the Client) and Gupta Electricals, Lajpat Rai Market, Delhi (the Contractor).\n\n1. Scope: the Contractor will maintain all electrical fittings at the Client's warehouse.\n\n2. Fee: Rs 2,40,000 per year, paid quarterly in advance within 7 days of invoice.\n\n3. Term: 12 months from 01-10-2026. The agreement renews automatically for another 12 months unless either party gives notice.\n\n4. Penalty: a late visit costs the Contractor Rs 5,000 per day, with no upper limit.\n\n5. Termination: either party may end this agreement with 90 days written notice.\n\n6. Jurisdiction: courts at Delhi only.\n\nSigned ...", "timing": { "bundle_ms": 0, "dpi": 120, "job_total_ms": 1986, "max_new_tokens": 3072, "pages": [ { "bundle_ms": 0, "chars": 762, "page": 1, "predict_ms": 1964, "render_ms": 20, "status": "ok", "total_ms": 1966 } ], "pages_processed": 1, "predict_ms": 1964, "queue_ms": 0, "render_ms": 20, "run_ms": 1986, "total_ms": 1986, "vl_max_concurrency": 2 }, "truncated": false, "warnings": [] }, "status": "done", "total_ms": 1986 }POST /v1/chat/completionsSmart1.2 sSmart returned JSON that matches the Review schema.
Request and response
Request { "messages": [ { "role": "system", "content": "You review business documents for an Indian company. Use only the document text. Dates as DD-MM-YYYY. List a risk only if the text supports it." }, { "role": "user", "content": "Document text:\n\n## SERVICE AGREEMENT\n\nThis agreement is made on 01-10-2026 between Sharma Traders, Chandni Chowk, Delhi (the Client) and Gupta Electricals, Lajpat Rai Market, Delhi (the Contractor).\n\n1. Scope: the Contractor will maintain all electrical fittings at the Client's warehouse.\n\n2. Fee: Rs 2,40,000 per year, paid quarterly in advance within 7 days of invoice.\n\n3. Term: 12 months from 01-10-2026. The agreement renews automatically for another 12 months unless either party gives notice.\n\n4. Penalty: a late visit costs the Contractor Rs 5,000 per day, with no upper limit.\n\n5. Termination: either party may end this agreement with 90 days written notice.\n\n6. Jurisdiction: courts at Del ..." } ], "response_format": { "type": "json_schema", "json_schema": { "name": "Review", "schema": { "properties": { "document_type": { "title": "Document Type", "type": "string" }, "parties": { "items": { "type": "string" }, "title": "Parties", "type": "array" }, "key_dates": { "items": { "type": "string" }, "title": "Key Dates", "type": "array" }, "amounts_inr": { "items": { "type": "string" }, "title": "Amounts Inr", "type": "array" }, "summary": { "title": "Summary", "type": "string" }, "risks": { "items": { "type": "string" }, "title": "Risks", "type": "array" } }, "required": [ "document_type", "parties", "key_dates", "amounts_inr", "summary", "risks" ], "title": "Review", "type": "object" }, "strict": true } } }Response 200 { "choices": [ { "finish_reason": "stop", "index": 0, "message": { "content": "{\"document_type\": \"SERVICE AGREEMENT\", \"parties\": [\"Sharma Traders\", \"Gupta Electricals\"], \"key_dates\": [\"01-10-2026\"], \"amounts_inr\": [\"Rs 2,40,000 per year\", \"Rs 5,000 per day\"], \"summary\": \"A service agreement between Sharma Traders and Gupta Electricals for the maintenance of electrical fittings at the Client's warehouse for a term of 12 months starting from 01-10-2026, with automatic renewal and a 90-day notice period for termination.\", \"risks\": [\"A late visit costs the Contractor Rs 5,000 per day, with no upper limit.\"]}", "role": "assistant" } } ], "object": "chat.completion", "usage": { "completion_tokens": 162, "prompt_tokens": 441, "total_tokens": 603 } }POST /generateSmart9.2 sMade a Word file: Service Agreement Review Note Sharma Traders Gupta Electricals.docx.
Request and response
Request { "prompt": "Create a Word document: a one-page review note for 'agreement-scan.pdf'. Use these facts only, with headings for Summary, Parties, Key dates, Amounts and Risks:\n{\n \"document_type\": \"SERVICE AGREEMENT\",\n \"parties\": [\n \"Sharma Traders\",\n \"Gupta Electricals\"\n ],\n \"key_dates\": [\n \"01-10-2026\"\n ],\n \"amounts_inr\": [\n \"Rs 2,40,000 per year\",\n \"Rs 5,000 per day\"\n ],\n \"summary\": \"A service agreement between Sharma Traders and Gupta Electricals for the maintenance of electrical fittings at the Client's warehouse for a term of 12 months starting from 01-10-2026, with automatic renewal and a 90-day notice period for termination.\",\n \"risks\": [\n \"A late visit costs the Contracto ...", "stream": false }Response 200 { "duration_ms": 8356, "office_artifact": { "artifact_id": "RhHWbuai1IfjZ8Z37zPqMYoU1PcGAbwD", "available": true, "created_at": "2026-10-07T00:29:58.771469Z", "expires_at": "2026-10-14T00:29:58.771469Z", "filename": "Service Agreement Review Note Sharma Traders Gupta Electricals.docx", "kind": "docx_report", "logical_pages": 1, "mime": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "page_count_mode": "logical", "size": 39161, "title": "Service Agreement Review Note: Sharma Traders & Gupta Electricals" }, "response": "Created Service Agreement Review Note Sharma Traders Gupta Electricals.docx." }GET /office/artifacts/{id}/download0.1 sDownloaded the file.
Request and response
Request (empty)
Response 200 <application/vnd.openxmlformats-officedocument.wordprocessingml.document, 39,161 bytes>
What the program printed
{
"document_type": "SERVICE AGREEMENT",
"parties": [
"Sharma Traders",
"Gupta Electricals"
],
"key_dates": [
"01-10-2026"
],
"amounts_inr": [
"Rs 2,40,000 per year",
"Rs 5,000 per day"
],
"summary": "A service agreement between Sharma Traders and Gupta Electricals for the maintenance of electrical fittings at the Client's warehouse for a term of 12 months starting from 01-10-2026, with automatic renewal and a 90-day notice period for termination.",
"risks": [
"A late visit costs the Contractor Rs 5,000 per day, with no upper limit."
]
}
Review note: Service Agreement Review Note Sharma Traders Gupta Electricals.docxFile made: Service Agreement Review Note Sharma Traders Gupta Electricals.docx (38 KB)
How it works
Section titled “How it works”- Exact first, OCR only when needed.
/pdfuses no model, so digital PDFs are read exactly and quickly. Only scans go to OCR. - OCR is a job. Start it, then check it every five seconds, with a deadline.
- The review is JSON. Your code can store it, search it, or send risky contracts to a senior person first.
- The Word file is checked. If the response has no
office_artifact, no file was made, and the agent says so instead of pretending.
Where else this works
Section titled “Where else this works”Vendor onboarding papers, bank statements, insurance claims, tender documents, purchase orders and
HR files. For tables, such as a bank statement, ask /pdf for "mode": "tables" and work with the rows
directly.
Common questions
How does the agent know if a PDF is scanned?
It first asks POST /pdf for the text inside the file. A digital PDF returns plenty of text. A scan returns little or none, because it is only a picture of a page, so the agent sends it to OCR instead.
How long does OCR take on a scanned PDF?
It depends on the number of pages and how busy your server is. OCR runs as a job, so your code checks it every few seconds. Always set a deadline, for example ten minutes, so a stuck job does not hold up the queue.