All posts
Product
#routing
#transparency
#receipts
#privacy

Routing you can read back: tool-aware lanes, rejection receipts, and privacy proofs

BatchRouter routing now respects the hosted tools your job needs, returns an itemized receipt for every lane it skipped, and attaches a per-lane data/privacy proof to every quote.

Routing Desk

Engineering

June 8, 2026 5 min read

Every batch you submit to BatchRouter gets quoted across every eligible provider lane. The router applies hard gates — task support, model, capacity, rate limits, privacy, region, price ceiling, trust tier — then ranks what's left and routes to the cheapest lane that can finish in your window. That's the product. The thing we've spent the last week shipping is the part around the decision: making it legible. Three upgrades landed, and they all push in the same direction — you should be able to read back exactly why a lane won, and why every other lane didn't.

Tool-aware routing

A growing share of batch work isn't just "run this prompt on this model." It needs hosted tools: web search, Python execution, calculator, time, file search, retrieval. Until now BatchRouter treated those as your problem to verify. Now they're a first-class routing input.

Provider offerings carry explicit hosted_tools metadata, kept separate from general runtime capabilities, and the marketplace, model detail pages, calculator, and quote receipts all read from those offering snapshots instead of static, hand-maintained claims. On a quote you declare what the job needs with required_tools, and routing rejects any lane that can't satisfy every required tool before price even enters the picture.

json
POST /v1/quotes/model
{
  "task": "responses",
  "models": ["gpt-4o-mini", "claude-haiku"],
  "required_tools": ["web_search", "python_execution"],
  "max_price": "...",
  "completion_window": "24h"
}
Quote-level required_tools apply to every item; item-level requirements merge on top.

Quote-level tool requirements apply to every planned item or workflow input; item-level values merge with the quote-level list; and supported OpenAI-style tool declarations can be inferred straight from item payloads, so you don't have to repeat yourself. The decision comes back on the route receipt as tool_support, with the required and offered tool lists side by side — so you can see not just that a lane qualified, but on which tools.

A receipt for every rejected lane

The cheapest lane that didn't win is often the more interesting one. Previously, a non-selected lane just disappeared from the result. Now BatchRouter persists a rejection receipt for every lane it considered and skipped, stored alongside the lane allocations and reservations for the quote.

Each receipt normalizes the underlying failure into a customer-safe reason code, while retaining the raw router code and the specific eligibility checks that failed underneath. The codes are deliberately readable:

  • capacity_full — the lane was eligible but had no fresh queue or token capacity for your job size
  • missing_web_search — the lane couldn't satisfy a required tool
  • context_window_exceeded — your estimated context didn't fit the lane's model
  • privacy_tier_mismatch — the lane didn't meet your data-handling requirement
  • region_unavailable — the lane couldn't serve your required region or residency

No more guessing why a job didn't route to the lane you expected. If a cheaper provider was on the table and got passed over, the receipt tells you the one thing you actually wanted to know: why.

Per-lane privacy proofs

Routing decisions and privacy posture used to live in different places — you'd pick a lane on price and then go read a provider's terms separately. We've collapsed that. Every quote response, billing receipt, usage receipt, and stored route receipt artifact now carries a data/privacy proof per lane — for the lane that was selected and for the ones that were rejected.

Each proof spells out the provider's data collection, ZDR support, retention in days, serving regions, training-use posture, privacy terms, and how BatchRouter handles the resulting artifacts. To keep that honest at the supply side, public offering lanes have to declare retention, ZDR, artifact handling, and regions up front; lanes missing those fields are hidden or rejected unless they're explicitly marked unverified — in which case they stay routable but carry a verification penalty and receipt-visible reasons.

Preflight: fail before you spend

Quote creation and direct batch submission now run the same server-side preflight validator before any provider gets quoted or any batch gets created. It checks the JSONL and input-manifest shape, that referenced files are yours and in a supported modality, that your estimated context fits, that hosted-tool and JSON-schema or runtime-capability requirements can be met, and that any webhook URL is safe.

When something's off, you get an error.details.preflight payload that itemizes the issues by category with a remediation action for each — and you get it before credits are reserved or work is created. A malformed batch fails in the cheapest possible place: immediately, for free.

One more thing: br_ API keys

Small but visible: API keys issued now carry a br_ prefix — part of finishing the move to the BatchRouter name. If you're scripting against the API, that's the shape your new keys will take.


One principle has been on the wall since we started: routing must be explainable — every quote should show why a lane or workflow product was selected. Tool-aware routing, rejection receipts, and per-lane privacy proofs are that principle turned into surfaces you can actually read. Submit a quote and check the route receipt; the reasoning is all there now.

Share X LinkedIn HN RSS

Routing Desk

Engineering

Posts from the team that designs the lane selector, retry policy, and capacity planner.

Keep reading

All posts