Routing
The four doors into a route — the generic ladder, signature nomination, named embedding spaces, and a supplied plan.
Routing decides which installed capability reads each page. It decides over capability kinds, never capability IDs. A third-party OCR distribution therefore routes exactly like the first-party one. A capability enters a route through exactly one of four doors. The kind it declares determines which door that is.
| Door | Kind it admits | When it applies | When it cannot |
|---|---|---|---|
| Generic ladder | native_extraction, ocr, model, manual_review |
Every plan. Page evidence picks the first rung; every rung below stays in the plan as a fallback. | A kind nothing installed declares is skipped and the ladder descends past it. |
| Signature nomination | parser |
The request sets signature_detection and the parser’s own detect() recognized the document. |
No match means the generic ladder alone. A media type only a parser declares, planned without detection, is an unsatisfied plan naming the parser and the flag. |
| Named embedding space | embedder |
The request lists embedding_space_ids. |
No fallback: a space the snapshot lacks is a 422, an embedder that cannot run right now is a 503. |
| Supplied plan | Whatever the plan already names | encode(plan=...) re-executes a decision made earlier. |
A source-digest or snapshot mismatch is a 409; execution never silently replans. |
The generic ladder
Section titled “The generic ladder”The ladder is the default policy’s escalation order. Each rung costs more to be right than the one above it:
native text layer → ocr → vision model → manual review
Preflight evidence picks the first rung per page. A usable text layer or an apparently empty page starts at free native extraction; a scanned page starts at recognition. Every rung below the selected one stays in the plan as an ordered fallback, so one capability failure costs an attempt rather than a page. Manual review is terminal. It declares no media types because a human reads anything, and it cannot fail over to anything else. That is what keeps unreadable content visible instead of silently dropped.
Availability is deliberately not consulted. A plan proposes work against the recorded inventory; whether a capability can run right now is an execution question, answered there with a 503 and a descent to the next fallback.
The ladder is closed
Section titled “The ladder is closed”Routing over kinds is what makes an extension routable. It is also the limit: the set of kinds is fixed at six, the escalation order at three rungs, and the signals the policy reads at two. Installing a distribution adds something to route to; it never adds a rung, a kind, or a meaning, because each of those is a decision about what indx is willing to pay for and in what order. Changing one is a first-party policy change with a POLICY_VERSION bump.
What that means if your work is none of the six — classification, redaction, translation, speech, table structure — is that you declare the nearest kind and inherit its ladder position, and the deployment corrects the cost, quality and latency through INDX_ROUTING_ECONOMICS. Those numbers are core-owned for the same reason a rung is: a capability that priced itself would be buying its way into a route. That cost is real and not hidden. A hosted OCR charging $0.05 per page is admitted, and estimated, on the OCR default of $0.0005 until an operator writes the real figure down.
The same closure applies to preflight signals. PageEvidence.signals is a tuple of free strings so an observer can report what it saw, and the ladder reads exactly two of them. An observer emitting handwriting is inert rather than wrong. That is why handwriting is still the benchmark’s single route-accuracy miss.
mixed-multipage.pdf document [native-extraction, generic-ocr, generic-vlm, manual-review] # usable text layer page:2 [generic-ocr, generic-vlm, manual-review] # scanned pageSignature nomination
Section titled “Signature nomination”A parser is a specialist that is only correct about a document something recognized first, so it never joins the ladder. When the request opts in with signature_detection, each installed parser’s detect() is asked. Detection stays within preflight’s budget: plan time, no OCR, no models, no rendering, no network. A match nominates that parser ahead of the generic ladder, never in place of it. A signature is a guess. Keeping the ladder behind the specialist means a wrong guess costs one cheap attempt rather than the document.
invoice-anthropic.pdf, signature_detection=true document [invoice-parser, native-extraction, generic-ocr, generic-vlm, manual-review]A signature scoped to a region nominates nothing. It is a claim about part of a page, and the plan holds document and page assignments only, so promoting it would read “there is an invoice table in this region” as “this page is an invoice” and send the whole page to the specialist. The match is still carried on the plan, so the caller learns what was recognized.
Nomination is a parser’s only door. A media type only a parser declares, planned without detection, comes back as an unsatisfied plan whose constraint names the parser and the flag — not a silent manual-review route. If your capability generically reads its format, declare native_extraction and ship the parser alongside it; see Specialist parser.
Named embedding spaces
Section titled “Named embedding spaces”An embedder reads no pages, so no rung ever reaches it. It runs because the caller named its space:
result = service.encode( EncodeRequest(..., embedding_space_ids=("default-text",)))There is no fallback ladder here, on purpose. A caller who named a space asked for exactly it. A silently different space would corrupt every vector comparison downstream. A space the snapshot does not hold is a 422; an embedder that cannot run is a 503, never a substitution. See Embedding space.
A supplied plan
Section titled “A supplied plan”A plan is a serializable artifact, so a route decided once can be inspected, approved, stored, and executed later. When encode() receives a plan, the executor verifies the source digest, the media type and the capability snapshot. A mismatch is a typed 409. Execution may run only the selected capability and the fallbacks that plan already names. It never silently replans. Whatever entered the plan through the doors above is the whole set of things allowed to run. See Interfaces & API.
Before routing
Section titled “Before routing”Three gates run before any door opens. Each is an installed distribution rather than a built-in. A source loader must resolve the URI scheme. A source observer must make the media type plannable. And some capability must declare it readable. Otherwise the answer is a typed 415 or 422 before routing begins. Extend indx covers all of them.