Routing
Processing regions: Switzerland, the EU, the US, or no preference
How much of your data leaves, which model catalogue is eligible, and which jurisdiction the work may stand in. They are three separate axes, they are resolved in one place each, and the answer is on the response — not in a support ticket.
Three axes
Orthogonal, not a single dial.
Conflating these is the classic mistake: “full-data” does not mean “PHI”, and “PHI” does not mean “EU”. Each axis has its own resolver, its own default, and its own failure direction.
First axis — how much data leaves
Schema-only sends column headers and at most three sample values per column, each truncated to 80 characters. Full-data lets the AI layer see rows, and is gated on an active subscription.
ChokepointclampForSchemaOnly() in lib/parser.ts, applied at the HTTP edge in every /api/v1 route that accepts sample rows and re-applied at the model boundary in lib/llm.ts.
Second axis — which catalogue is eligible
Standard runs on our own pinned general models. PHI · enterprise sends X-PHI to phi-cloud, which then forces a PHI-eligible model. This axis is orthogonal to the one beside it — neither implies the other.
ResolverresolveRunPhi() in lib/agreements.ts, threaded as phiOverride into match(), runTransform() and runReshapeRequest().
Third axis — where compute may stand
The workspace region pin travels on standard runs too. PHI decides which models are eligible; the region decides which ground they are allowed to run on, and the program sandbox refuses a run with no placeable jurisdiction.
RuleZeroing the region on a standard run made every standard code-strategy reshape fail. It is pinned on both modes now — see /network.
- Standard is the default
- PHI locked behind the BAA
- Pinned ids, never a silent auto-route
- +20% follows the actual routing
- Host allowlist fails closed
The decision
One resolver, and it defaults to the cheaper answer.
Until 2026-09-01 an unconfigured workspace defaulted to PHI. That was reversed: standard is now what you get, and PHI is an explicit opt-in alongside accepting the BAA. Every fail-soft path resolves to standard too — an unreadable settings row must never silently upgrade unknown traffic into a surcharge and an agreement gate.
One resolver
resolveRunPhi(tenantId, requested) → lib/agreements.ts
- 1phi_mode in the request body — Chat’s Standard / PHI · enterprise switch, or your own API call.
- 2Otherwise tenants.phi_mode, the workspace default.
- 3Otherwise standard — and that is also where an unreadable settings row lands.
- 4An explicit PHI ask with no BAA acceptance on file is 403 agreement_required, with a pointer to the settings page. Never a silent downgrade.
Standard
the default- Headers
- X-Region only. No X-PHI is sent.
- Model
- A pinned id from our own three-rung ladder, resolved by modelFor().
- Region
- US for the ladder call; the workspace pin still governs sandbox placement.
- Price
- No uplift.
PHI · enterprise
explicit opt-in- Headers
- X-PHI: true plus X-Region set to your workspace pin.
- Model
- Named by phi-cloud from the residency-locked PHI catalogue. We name nothing.
- Region
- Your pin. The host must also be on the BAA allowlist or the call is refused.
- Price
- +20% on the whole charge.
What goes on the wire
Two header sets, one endpoint.
Both branches call the same phi-cloud endpoint. The difference is two headers and whether we name a model at all. A standard call carries a concrete pinned id and the region that serves it; a PHI call carries the data class and the jurisdiction and lets the gateway that holds the BAA make the pick.
The allowlist sits underneath both: MAPR_PHI_ALLOWED_HOSTS names the hostnames permitted to receive full-data traffic, and in production an unset allowlist refuses rather than permits. See the fail-closed gates.
# Standard routing — the default for an unconfigured workspace.
POST /v1/chat/completions → phi-cloud
X-Region: US # the region that SERVES the pinned id
model: inception-mercury-2 # low rung, resolved by modelFor()
# no X-PHI header at all
# PHI · enterprise routing — only after the BAA is accepted in-app.
POST /v1/chat/completions → phi-cloud
X-PHI: true # forces the PHI-eligible catalogue
X-Region: CH # the workspace pin
model: auto # phi-cloud picks, in-regionThe general ladder
Three rungs for traffic that is not regulated.
Non-PHI work runs on models we pin by id. The rungs are a capability and latency ladder, not a price ladder — and the ids are pinned precisely so nobody, including us, can quietly re-route them.
| Rung | Model | Override |
|---|---|---|
| lowThe default. Most turns, and every call that names no tier. | inception-mercury-2 | MAPR_GENERAL_MODEL_LOW |
| midOne real piece of work. | zai-glm-5-3-flash | MAPR_GENERAL_MODEL_MID |
| highThe rung a tier:"quality" codegen or critic turn is promoted to. | zai-glm-5-3 | MAPR_GENERAL_MODEL_HIGH |
The ids are pinned on purpose
phi-cloud’s own router is price-ordered, and the low-rung model is not anyone’s default. Leaving the choice to the gateway would mean a general call quietly landing on the mid rung and the low rung never running at all.
ConsequenceA pinned id must ship the region that serves it. Without X-Region the request reads as WORLD, matches no model, and auto-routes — a 200 from a model nobody chose.
The ladder is not price-ordered
The low rung is a latency pick and costs more per token than the middle rung. Read the rungs as capability and latency, never as “low is the cheap one” — that misreading is how a cost model ends up backwards. Every rung is overridable, and MAPR_GENERAL_LADDER=off drops back to a single configured model everywhere.
PHI is deliberately not on it
A PHI call names no model. It sends X-PHI and X-Region and takes phi-cloud’s residency-locked pick, because model ids for regulated traffic belong on the gateway that enforces the BAA — not in an application repository.
WheremodelFor() in lib/models.ts is the one resolver both policies go through, so a call site cannot let them drift apart.
Heavy turns are promoted
A codegen or critic turn asks for the quality tier, and rungForTier() is what turns that request into the top rung. The tier the call asked for and the model it lands on stay one decision rather than two that can disagree; MAPR_CODEGEN_TIER controls it, and “none” disables the promotion.
What routing costs
The uplift follows the routing a run actually got.
Not the routing it asked for, and not the policy the workspace has set. If a run was downgraded to standard for any reason, it is billed as standard and flagged as downgraded — that is a compliance rule wearing a pricing rule's clothes.
| What is charged | Rate |
|---|---|
| Every map, deterministic or notCharged even when the cascade resolves every column without touching a model, and on a layout-fingerprint cache hit that uses no AI at all. | Flat per-map fee (~$0.001) |
| AI cleanup, convert, reshapeThe AI tokens actually consumed, on top of the flat fee. × 0.5 on the token component when you bring your own LLM key. | Flat fee + provider cost × 2 |
| Macro authoringA delegated VBA edit draws the same flat fee a map does, and nothing else: the settle call passes zero prompt tokens, zero completion tokens and zero provider cost, so the delegated turn’s own tokens are not metered back to you. Reading a workbook’s macros — modules, entry points, risky-call signals — is deterministic and free. | The flat per-map fee |
| PHI · enterprise routingFlat fee included. Applied only when the run actually routed as PHI — see the rule below. | +20% on the whole charge |
# The kill switch that makes this rule necessary.
MAPR_RESHAPE_DISABLE_PHI=1
# It strips X-PHI from the sandbox execution call. The run therefore
# did NOT go through the PHI path, so effectiveRunPhi() returns false:
charge standard rate, no 20% uplift
audit payload phi_downgraded: true
response a user-visible warning saying so
# Charging a BAA premium for a run that bypassed the BAA gate is a
# claim we cannot make — /pricing sells the uplift as "routed
# in-region under a BAA", and this run was not.Why this matters
A premium you cannot justify is a claim you should not make.
The 20% is sold as “routed in-region under a BAA”. If an operator kill-switch strips the PHI header from a sandbox execution, that particular sentence stops being true for that particular run — so the run is charged at the standard rate, phi_downgraded lands in the audit payload, and you see a warning in the result.
It would have been easier to bill the requested policy. It would also have been an invoice line describing something that did not happen. See the full pricing model and the BAA gate.
Standard by default. PHI when you say so.
Flip the switch per run in Chat, or send phi_mode in the request body. Either way the gate reads your acceptance before anything moves.