{"openapi":"3.1.0","info":{"title":"Docira","description":"Multi-model OCR/document-to-Markdown/JSON API","version":"0.1.0"},"paths":{"/v1/parse":{"post":{"summary":"Parse Document","description":"Parse a single document from a URL.\n\nDownloads the document, runs the full pipeline (ingestion → classification\n→ routing → VLM → verification → delivery), and returns the result\nsynchronously.","operationId":"parse_document_v1_parse_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseURLRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/parse/upload":{"post":{"summary":"Parse Upload","description":"Parse an uploaded document file.\n\nAccepts multipart file upload, runs the full pipeline, and returns\nthe result synchronously, OR an SSE stream when the client sends\n``Accept: text/event-stream`` and the LangGraph runtime is active\n(W3-T03).","operationId":"parse_upload_v1_parse_upload_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"legacy_events","in":"query","required":false,"schema":{"type":"boolean","description":"When using SSE (Accept: text/event-stream), drop W3-only events (node.started/node.completed) so the wire matches the W0 dict-event subset. Temporary back-compat shim; removed in W4.","default":false,"title":"Legacy Events"},"description":"When using SSE (Accept: text/event-stream), drop W3-only events (node.started/node.completed) so the wire matches the W0 dict-event subset. Temporary back-compat shim; removed in W4."},{"name":"include_warnings","in":"query","required":false,"schema":{"type":"boolean","description":"When True, include the per-page ``warnings`` field (W5b post-processing). Default False preserves the pre-W5b wire shape.","default":false,"title":"Include Warnings"},"description":"When True, include the per-page ``warnings`` field (W5b post-processing). Default False preserves the pre-W5b wire shape."},{"name":"extraction_strict","in":"query","required":false,"schema":{"type":"boolean","description":"When True, schema-validation failures convert the response to HTTP 422 with the errors as the body. Default False preserves the warning-mode contract (errors attached to the 200 response when DOC_USE_OUTPUT_VALIDATION is on).","default":false,"title":"Extraction Strict"},"description":"When True, schema-validation failures convert the response to HTTP 422 with the errors as the body. Default False preserves the warning-mode contract (errors attached to the 200 response when DOC_USE_OUTPUT_VALIDATION is on)."}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_parse_upload_v1_parse_upload_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/parse/stream":{"post":{"summary":"Parse Upload Stream","description":"Same as /parse/upload but streams pipeline events as SSE.\n\nEach event is one JSON line prefixed with ``data: `` per the\nServer-Sent-Events spec. The final event is always ``result.final``\ncarrying the full ParseResponse for the client to consume.","operationId":"parse_upload_stream_v1_parse_stream_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_parse_upload_stream_v1_parse_stream_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/compare":{"post":{"summary":"Parse Compare","description":"Run the same document through 2–4 providers in parallel.\n\nReturns one merged payload with each run tagged by provider+model so\nthe client can render side-by-side comparison and a chunk-level diff.","operationId":"parse_compare_v1_compare_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_parse_compare_v1_compare_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Parse Compare V1 Compare Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/parse/estimate":{"post":{"summary":"Parse Estimate","description":"Estimate per-page tier mix and projected cost without parsing.\n\nWorkflow:\n    1. Read upload (capped at settings.max_upload_mb)\n    2. Ingest → list[PageImage] (rasterize + format detect)\n    3. Classify each page → complexity score\n    4. Map each score → tier via the same FAST_CEILING/PRO_CEILING\n       thresholds the live pipeline uses\n    5. Apply per-tier $/page pricing (the \"what we'd charge you\"\n       number, not the \"what we pay vendors\" number)\n    6. Return per-page breakdown + summary","operationId":"parse_estimate_v1_parse_estimate_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_parse_estimate_v1_parse_estimate_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Parse Estimate V1 Parse Estimate Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/parse/structured":{"post":{"summary":"Parse Structured","description":"Extract tables, equations, and figures as typed structured blocks.\n\nRuns ingestion → SmolDocling classification → per-region VLM\nextraction in parallel → Tier-0 sanity checks.  Returns a\n``StructuredDocument`` alongside usage, timing, and any sanity\nwarnings.\n\nReturns HTTP 422 when the document contains no extractable regions\n(tables / equations / figures) — callers should use ``/v1/parse/upload``\nfor plain-text documents.","operationId":"parse_structured_v1_parse_structured_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_parse_structured_v1_parse_structured_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Parse Structured V1 Parse Structured Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/parse/batch":{"post":{"summary":"Parse Batch","description":"Submit a batch of documents for asynchronous processing.\n\nReturns a batch ID immediately. Documents are processed in the\nbackground. Results are delivered via webhook or polling.\n\nMaximum batch size is governed by settings.max_batch_documents (default 100).","operationId":"parse_batch_v1_parse_batch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_BatchParseRequestDynamic"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Parse Batch V1 Parse Batch Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/parse/{request_id}":{"get":{"summary":"Get Parse Result","description":"Retrieve the result of a parse request by ID.\n\nReturns the full ParseResponse if available, or status information\nif the request is still processing. Returns 404 for both \"not found\"\nand \"found but belongs to another user\" to avoid existence leakage.","operationId":"get_parse_result_v1_parse__request_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Parse Result V1 Parse  Request Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/parse/{request_id}/cancel":{"post":{"summary":"Cancel Parse","description":"Cancel an in-flight parse. Per ADR-0020 §\"4c\":\n\n  * Auth required (any tier)\n  * Rate-limited per RateLimiter at the same RPM as the parse\n    endpoints (the cap is at the auth layer; this route counts\n    against the same key budget)\n  * Owner check: 404 if the request_id is unknown OR belongs\n    to another user (no body discrimination — OWASP A01)\n  * On success: cancels the live asyncio.Task, deletes the\n    checkpoint thread (so resume returns 404), returns 202\n\nReturns 404 if the parse is not in flight (already completed,\nalready cancelled, or never existed). Returns 202 Accepted on\nsuccessful cancellation — the customer's pending GET on the\nparse's result will see whatever the cancellation produced\n(typically nothing; the cancelled task didn't reach the\nresult_store save).","operationId":"cancel_parse_v1_parse__request_id__cancel_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Cancel Parse V1 Parse  Request Id  Cancel Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/parse/{request_id}/resume":{"post":{"summary":"Resume Parse","description":"Resume a checkpointed parse to completion. Per ADR-0020 §\"4c\":\n\n  * Auth required\n  * Owner check: 404 if the request_id has no result_store\n    record AND no in-flight task — OR — the existing record\n    belongs to another user (per QA-04c-002, identical 404\n    response shape on miss + ownership mismatch)\n  * Saver lookup: 404 if no checkpoint exists (flag-OFF\n    deployments, expired TTL, cancelled-and-deleted)\n  * On success: returns the hydrated ParseResponse and\n    re-saves to result_store\n\nIdempotent under contention: 5 parallel resumers serialise on\nLangGraph's per-thread lock; all return identical state.","operationId":"resume_parse_v1_parse__request_id__resume_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/batch/{batch_id}":{"get":{"summary":"Get Batch Status","description":"Retrieve the status of a batch processing job.","operationId":"get_batch_status_v1_batch__batch_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"batch_id","in":"path","required":true,"schema":{"type":"string","title":"Batch Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Batch Status V1 Batch  Batch Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/live":{"get":{"summary":"Liveness Probe","description":"Liveness probe — process is up.\n\nAlways returns 200 unconditionally. This is the Kubernetes / Fly\nliveness probe: it answers \"is the Python process alive and the\nASGI app accepting connections?\" — distinct from readiness, which\nis what ``/v1/health`` measures.\n\nNo auth required; no provider state inspected. M1-02 (slice 2)\nadded this so deployment platforms can distinguish \"restart me\"\n(live=fail) from \"drain me but keep me running\" (live=ok, ready=fail).","operationId":"liveness_probe_v1_live_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Liveness Probe V1 Live Get"}}}}}}},"/v1/health":{"get":{"summary":"Health Check","description":"Readiness probe (no auth required).\n\nReturns HTTP 503 when status is ``unhealthy`` so Fly's load\nbalancer drains traffic from machines whose providers are all\nfailing. ``healthy`` and ``degraded`` both return 200 — degraded\nmachines should still receive traffic (they're processing some\nrequests successfully).\n\nFor \"is the process alive\" use ``GET /v1/live`` — this endpoint\nalso answers that, but with provider-state context that is not\navailable before the first request lands.","operationId":"health_check_v1_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/v1/providers":{"get":{"summary":"Provider Status","description":"Return the status of all configured providers with circuit breaker states.","operationId":"provider_status_v1_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Provider Status V1 Providers Get"}}}}},"security":[{"APIKeyHeader":[]}]}}},"components":{"schemas":{"BBox":{"properties":{"x":{"type":"number","minimum":0.0,"title":"X","description":"Left edge, pixels from page left"},"y":{"type":"number","minimum":0.0,"title":"Y","description":"Top edge, pixels from page top"},"w":{"type":"number","exclusiveMinimum":0.0,"title":"W","description":"Width in pixels"},"h":{"type":"number","exclusiveMinimum":0.0,"title":"H","description":"Height in pixels"},"label":{"type":"string","title":"Label","description":"Element type: paragraph, table, figure, header, footer, list, code, formula, handwriting"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","description":"SmolDocling detection confidence"}},"type":"object","required":["x","y","w","h","label","confidence"],"title":"BBox","description":"Bounding box for a detected document element.\n\nCoordinates are in pixels relative to the top-left corner of the page image."},"Body_parse_compare_v1_compare_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"providers":{"type":"string","title":"Providers"},"operation_mode":{"type":"string","title":"Operation Mode","default":"ocr"},"max_pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Pages"}},"type":"object","required":["file","providers"],"title":"Body_parse_compare_v1_compare_post"},"Body_parse_estimate_v1_parse_estimate_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"max_pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Pages"}},"type":"object","required":["file"],"title":"Body_parse_estimate_v1_parse_estimate_post"},"Body_parse_structured_v1_parse_structured_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"max_pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Pages"},"model":{"type":"string","title":"Model","default":"claude-sonnet-4-6"}},"type":"object","required":["file"],"title":"Body_parse_structured_v1_parse_structured_post"},"Body_parse_upload_stream_v1_parse_stream_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"operation_mode":{"type":"string","title":"Operation Mode","default":"ocr"},"maintain_format":{"type":"boolean","title":"Maintain Format","default":true},"max_pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Pages"},"extraction_schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Extraction Schema"},"enable_verification":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Verification"},"dpi":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Dpi"}},"type":"object","required":["file"],"title":"Body_parse_upload_stream_v1_parse_stream_post"},"Body_parse_upload_v1_parse_upload_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"},"operation_mode":{"type":"string","title":"Operation Mode","default":"ocr"},"maintain_format":{"type":"boolean","title":"Maintain Format","default":true},"max_pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Pages"},"enable_verification":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Verification"},"dpi":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Dpi"}},"type":"object","required":["file"],"title":"Body_parse_upload_v1_parse_upload_post"},"ConfidenceReport":{"properties":{"layout_score":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Layout Score","description":"Quality of document element recognition"},"ocr_score":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Ocr Score","description":"Model confidence derived from logprobs"},"verbatim_score":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Verbatim Score","description":"Fidelity to source text (1 - edit_distance)"},"table_score":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Table Score","description":"Table structural integrity (None if no tables)"},"mean_grade":{"$ref":"#/components/schemas/QualityGrade","description":"Aggregate quality grade"},"low_grade":{"$ref":"#/components/schemas/QualityGrade","description":"Grade of the worst-performing dimension"}},"type":"object","required":["layout_score","ocr_score","verbatim_score","mean_grade","low_grade"],"title":"ConfidenceReport","description":"Multi-signal quality assessment for a processed page.\n\nEach score is in the range [0.0, 1.0] where 1.0 is perfect."},"DocumentFormat":{"type":"string","enum":["pdf","docx","pptx","xlsx","xls","png","jpeg","heic","tiff","webp"],"title":"DocumentFormat","description":"Supported input document formats."},"DocumentMetadata":{"properties":{"hash":{"type":"string","title":"Hash","description":"SHA-256 of original document bytes"},"pages":{"type":"integer","minimum":0.0,"title":"Pages"},"format":{"$ref":"#/components/schemas/DocumentFormat"},"cached":{"type":"boolean","title":"Cached","default":false},"filename":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filename"},"size_bytes":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Size Bytes"}},"type":"object","required":["hash","pages","format"],"title":"DocumentMetadata","description":"Metadata about the original input document."},"DocumentTypeReport":{"properties":{"detected":{"type":"string","title":"Detected","description":"One of: textbook, academic_paper, slide_deck, invoice_or_form, report_manual, image_or_scan, unknown"},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence"},"rationale":{"type":"string","title":"Rationale","description":"Human-readable explanation of the decision"},"suggested_dpi":{"type":"integer","title":"Suggested Dpi"},"suggested_enable_verification":{"type":"boolean","title":"Suggested Enable Verification"}},"type":"object","required":["detected","confidence","rationale","suggested_dpi","suggested_enable_verification"],"title":"DocumentTypeReport","description":"Per-document type detection surfaced in the API response.\n\nLets callers see which preset the system used (or would have used)\nso they can tune their own pipeline knobs explicitly."},"ExtractionValidationError":{"properties":{"pointer":{"type":"string","title":"Pointer","description":"JSON Pointer (RFC 6901) to the failing field, e.g. '/customers/0/name'. Empty string for top-level errors."},"schema_rule":{"type":"string","title":"Schema Rule","description":"Name of the schema rule that failed (e.g. 'required', 'type', 'pattern', 'enum'). Lets the customer route based on failure category."},"message":{"type":"string","title":"Message","description":"Human-readable summary. NEVER includes the offending value (could be PHI); references the field by pointer instead."}},"additionalProperties":false,"type":"object","required":["pointer","schema_rule","message"],"title":"ExtractionValidationError","description":"One schema-violation report. Carried on\n``ParseResponse.errors[].extraction_validation``.\n\nFrozen + extra='forbid' so the wire contract is locked."},"GroundingEntry":{"properties":{"text":{"type":"string","title":"Text","description":"The extracted text span"},"bbox":{"$ref":"#/components/schemas/BBox","description":"Source bounding box on the original page"},"element_type":{"type":"string","title":"Element Type","description":"Structural element type"},"page_number":{"type":"integer","minimum":1.0,"title":"Page Number"}},"type":"object","required":["text","bbox","element_type","page_number"],"title":"GroundingEntry","description":"Maps an extracted text span back to its source location on the page image."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthResponse":{"properties":{"status":{"type":"string","title":"Status"},"version":{"type":"string","title":"Version"},"uptime_seconds":{"type":"number","title":"Uptime Seconds"},"providers":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Providers"}},"type":"object","required":["status","version","uptime_seconds"],"title":"HealthResponse","description":"Health check response.\n\n``providers`` is omitted entirely from the unauthenticated ``/health``\nalias to avoid leaking configured provider names to external crawlers\n(DOCIRA_ENGINEERING_STRATEGY §14.3 F-14). ``/v1/health`` still includes it."},"ParseResponse":{"additionalProperties":true,"type":"object"},"ParseURLRequest":{"properties":{"file_url":{"type":"string","title":"File Url"},"operation_mode":{"type":"string","title":"Operation Mode","default":"ocr"},"extraction_schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Extraction Schema"},"maintain_format":{"type":"boolean","title":"Maintain Format","default":true},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url"},"max_pages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Pages"},"compare_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Compare Provider"},"preferred_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preferred Model"}},"type":"object","required":["file_url"],"title":"ParseURLRequest","description":"Request body for URL-based document parsing."},"PostprocessWarning":{"properties":{"kind":{"type":"string","enum":["fence_stripped","heading_renumbered","list_normalized","duplicate_region","potential_hallucination"],"title":"Kind"},"severity":{"type":"string","enum":["info","warn","error"],"title":"Severity","default":"info"},"page_number":{"type":"integer","minimum":1.0,"title":"Page Number"},"detail":{"type":"string","title":"Detail","description":"Human-readable summary; never the full content"}},"additionalProperties":false,"type":"object","required":["kind","page_number","detail"],"title":"PostprocessWarning","description":"A single post-processing event surfaced to the customer.\n\nFrozen + extra='forbid' — the warning shape is a public API\ncontract. Mutating after construction would let a downstream\ntransform alter another transform's report."},"QualityGrade":{"type":"string","enum":["excellent","good","fair","poor"],"title":"QualityGrade","description":"Aggregate quality assessment for a processed page."},"Tier":{"type":"string","enum":["fast","pro","premium"],"title":"Tier","description":"Processing tier — maps to model capability and cost."},"TimingSummary":{"properties":{"total_ms":{"type":"integer","minimum":0.0,"title":"Total Ms"},"classification_ms":{"type":"integer","minimum":0.0,"title":"Classification Ms"},"routing_ms":{"type":"integer","minimum":0.0,"title":"Routing Ms"},"vlm_processing_ms":{"type":"integer","minimum":0.0,"title":"Vlm Processing Ms"},"verification_ms":{"type":"integer","minimum":0.0,"title":"Verification Ms"}},"type":"object","required":["total_ms","classification_ms","routing_ms","vlm_processing_ms","verification_ms"],"title":"TimingSummary","description":"Latency breakdown for the entire document processing."},"TokenUsage":{"properties":{"input_tokens":{"type":"integer","minimum":0.0,"title":"Input Tokens"},"output_tokens":{"type":"integer","minimum":0.0,"title":"Output Tokens"}},"type":"object","required":["input_tokens","output_tokens"],"title":"TokenUsage","description":"Token consumption for a single VLM call."},"UsageSummary":{"properties":{"total_input_tokens":{"type":"integer","minimum":0.0,"title":"Total Input Tokens"},"total_output_tokens":{"type":"integer","minimum":0.0,"title":"Total Output Tokens"},"estimated_cost_usd":{"type":"number","minimum":0.0,"title":"Estimated Cost Usd"},"pages_by_tier":{"additionalProperties":{"type":"integer"},"type":"object","title":"Pages By Tier","description":"Count of pages processed by each tier"}},"type":"object","required":["total_input_tokens","total_output_tokens","estimated_cost_usd"],"title":"UsageSummary","description":"Aggregate token usage and cost for the entire document."},"VLMResult":{"additionalProperties":true,"type":"object"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"_BatchParseRequestDynamic":{"properties":{"documents":{"items":{"$ref":"#/components/schemas/ParseURLRequest"},"type":"array","maxItems":100,"minItems":1,"title":"Documents"},"webhook_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Url"},"priority":{"type":"string","title":"Priority","default":"normal"}},"type":"object","required":["documents"],"title":"_BatchParseRequestDynamic","description":"Internal: applies the settings-driven batch cap at validation time."}},"securitySchemes":{"APIKeyHeader":{"type":"apiKey","in":"header","name":"X-API-Key"}}}}