POST/v1/route
Request
{ "policy": "support-summaries", "messages": [ { "role": "user", "content": "…" } ], "max_tokens": 300}
200 routed to gannet-s
API integration
Point your client at one endpoint and name a policy. Every response returns its route, measured latency and an energy estimate.
POST /v1/routePOST/v1/route
Request
{ "policy": "support-summaries", "messages": [ { "role": "user", "content": "…" } ], "max_tokens": 300}
200 routed to gannet-s
Keep the body, the retries and the timeouts. Send the call to the gateway and name a policy.
Call /v1/route on the gateway instead of your provider's URL.
Name the policy where the model name was. It picks the route for each request.
Messages, token limits and sampling settings pass to the route as you wrote them.
Removed: curl "$PROVIDER_URL/v1/chat" \Added: curl "$JOULIS_GATEWAY/v1/route" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{Removed: "model": "gannet-l",Added: "policy": "support-summaries", "messages": [ { "role": "system", "content": "Summarize in three sentences." }, { "role": "user", "content": "Ticket 4812: export stops at 500 rows" } ], "max_tokens": 300, "temperature": 0.2 }'
import os, requestsRemoved: resp = requests.post(f"{PROVIDER_URL}/v1/chat",Added: resp = requests.post(f"{JOULIS_GATEWAY}/v1/route", headers={"Authorization": f"Bearer {os.environ['API_KEY']}"}, json={Removed: "model": "gannet-l",Added: "policy": "support-summaries", "messages": [ {"role": "system", "content": "Summarize in three sentences."}, {"role": "user", "content": ticket_text}, ], "max_tokens": 300, }, timeout=10,)meta = resp.json()["joulis"]
const res = await fetch(Removed: `${PROVIDER_URL}/v1/chat`,Added: `${JOULIS_GATEWAY}/v1/route`, { method: "POST", headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({Removed: model: "gannet-l",Added: policy: "support-summaries", messages: [ { role: "system", content: "Summarize in three sentences." }, { role: "user", content: ticketText }, ], max_tokens: 300, }), },);const { joulis } = await res.json();
Responsethe model output as before, plus one joulis object
Errors
A policy exception is decided before any model call. The body names every candidate route and the budget it misses.
executed: falseNo model was called, so no inference was spent.
candidatesEach eligible route, with every budget it misses and by how much.
fallback: nullNo fallback named, so the request returns to your service.
422policy_exception
Before execution
{ "error": { "type": "policy_exception", "policy": "support-summaries-fast", "executed": false, "candidates": [ { "route": "gannet-l", "misses": [ { "budget": "p95_ms", "value": 1600, "limit": 1200 }, { "budget": "energy_wh", "value": 80, "limit": 60, "est": true } ] }, { "route": "gannet-s", "misses": [ { "budget": "p95_ms", "value": 1300, "limit": 1200 } ] } ], "fallback": null }}
404Nothing ran
unknown_policy
The policy id is not defined in this workspace. Check the spelling or publish the policy first.
503Nothing ran
provider_unavailable
No qualifying route’s provider answered and the policy names no fallback. Retry after the stated delay.
200Fallback ran
notice: provider_unavailable
The provider did not answer and your named fallback ran. The response says so with fallback_used: true.
When no route qualifies, the policy decides: run the fallback you named, or return the request unexecuted.
No route qualifies for support-summaries-fast
Policy sets"fallback": "gannet-l"
"joulis": { "route": "gannet-l", "policy": "support-summaries-fast", "fallback_used": true, "latency_ms": 1540, "p95_budget": "over", "energy_wh_est": 0.40, "exceeded_budgets": ["latency_p95"]}
Your service gets an answer and a record of the overrun, ready for your own reporting.
Policy sets"fallback": null
"error": { "type": "policy_exception", "policy": "support-summaries-fast", "message": "No route meets this policy", "executed": false, "candidates": [ … ], "fallback": null}
Your service decides what happens next: relax a budget, retry later or hold the rollout.

Bring one workload. We will run it under your policy and show you what each route delivers and consumes.