OpenAI-compatible endpoint
Point the official OpenAI SDK at the Cloud Score gateway: POST /v1/chat/completions routes when model is auto, pins when it is a model key, accepts dex_strategy, and answers with a dex_routing block, HTTP 403 on a block, 409 when no tool-capable model is available, and 502 on any other failure.
On this page
Prompt Optimization: optimize, review, then run
Prompt Optimization has one ON/OFF control, defaults ON, and accepts any prompt length. In Playground, Optimize prompt prepares a complete optimized prompt before answer routing, at the level you pick — Standard, Balanced or Thorough. Live counts use a real backend tokenizer. Review the original, optimized version and diff, then choose Run original or Run optimized. Our checks cannot guarantee that meaning is preserved, which is why you review the result. Editing the request invalidates its comparison. Workspace defaults are under Optimization › Settings.
For an interactive native flow, POST the full request to /v1/prompt-optimizations/prepare as {"request_type":"native","request":{...}} using the same bearer execution key. Include authoring options with the optimization strength (deterministic, balanced or aggressive) and the editable message indices. Then send the unchanged source request to /v1/requests with constraints.prompt_optimization containing enabled, proposal_id, the sealed proposal_token and choice (original or optimized). Use a distinct Idempotency-Key for preparation and execution. Keep the sealed token in memory only. Explicit {"enabled":false} preserves the original. Legacy compression fields are deprecated compatibility aliases; do not combine conflicting old and new controls.
Playground explicitly requests a bounded optimization and shows its recorded cost. A rejected, unavailable or unhelpful result falls back to your original without hiding its expense. Requests sent ON without a prepared proposal are optimized by structural rules only. Comparison details show the outcome, the rules applied and the measured work time. Infrastructure still consumes resources; unallocated compute keeps fully loaded savings unknown. Historical optimization expenses remain recorded and are not reset to zero.
The execution receipt distinguishes the selected prompt from the prompt actually sent for the answer, including no provider send when a cache supplies it. Optimization can have a recorded cost even when the original is selected or no answer is generated. Unknown infrastructure cost keeps fully loaded net unknown. Fewer tokens do not establish a latency or quality improvement. Analytics, ROI and Reports reuse Prompt Optimization evidence with production/demo filters and daily, weekly and monthly views.
See Playground for comparison and recovery, and Analytics for measurement coverage. Historical compression receipts do not establish choices or costs that were never recorded.
What it is
POST /v1/chat/completions is the gateway endpoint for code that already speaks the OpenAI API. You keep the official OpenAI SDK and change two things: the base URL and the key. Every call then runs the same pipeline as the native endpoint, is policy-checked, logged, and billed, and comes back with the standard usage and finish_reason fields plus an extra dex_routing block.
Your application
existing OpenAI SDK calls, unchanged
OpenAI SDK
base URL set to the gateway, key dex_sk_…
POST /v1/chat/completions
model "auto" routes, a model key pins
Cloud Score pipeline
guard, classify, match, policy, cache, select, execute, record
Chat completion
usage and finish_reason plus dex_routing
| Method | Path | Purpose |
|---|---|---|
POST | /v1/chat/completions | OpenAI-compatible. model: "auto" routes; a concrete name pins. |
GET | /v1/models | Models enabled for your workspace, with capabilities and pricing. |
The native endpoint /v1/requests adds strategies, constraints, the decision trace, and SSE streaming; see Send requests through Cloud Score. The snippet generator's blurb sums up the difference: Official OpenAI SDK pointed at the gateway — drop-in for existing OpenAI code.
Point the SDK at the gateway
The snippet generator on Admin › Developer writes the SDK call for you with the base URL and the key variable filled in.
Click
DeveloperunderAdminin the sidebar.The
Developerpage opens. ItsQuick startcard lists the three things you need:Create an API key,Export it in your shell,Copy the snippet below and run it.Click
Admin → API keys, create a key, and copy itsdex_sk_…value.The full key is shown once. See API keys for the options on the key, including the security profile and the budget the request will run under.
Export the key in your shell.
shellexport DEX_API_KEY="dex_sk_…"Under
Integration snippet, clickOpenAI-compatible, thenPython,TypeScript, orGo.The code block header reads
Python · OpenAI-compatible(or your language). The blurb switches to the OpenAI SDK wording quoted above.Leave
Intelligent routingand theAutotile selected.In this mode the snippet always sends
model: "auto", so the workspace routes every call. Pinning a model is theExplicit modelmode, covered below.Set
Base URLif the gateway is not onhttp://localhost:7001.A trailing slash is stripped. The URL must be reachable from the machine that runs the snippet, which is the published host port, not a compose-network hostname.
Click
Copy, paste the snippet into your project, and run it.The button reads
Copiedfor 1.5 seconds. A blocked request surfaces as an HTTP 403 and any other failure as a 502, raised by the SDK asAPIStatusErrororOpenAI.APIError.
Authentication
Send the key on every request as Authorization: Bearer dex_sk_…. Invalid and disabled keys get the same 401, missing or invalid API key, and every authenticated call updates the key's last-used timestamp, is policy-checked, logged, and billed. Keys carry no per-model restrictions; model availability is workspace-level.
Runtime needs of the copied snippet
The Python snippets need Python 3.10+ and httpx and set a 60 s HTTP timeout. The TypeScript snippet must run as an ES module (it uses top-level await). The Go snippet requires github.com/openai/openai-go. TypeScript and Go set no explicit timeout.
Request shape
A standard chat-completion body, with two Cloud Score extensions. The example below asks for the cost strategy. The Playground does not send this endpoint; it uses the native endpoint only.
{
"model": "auto",
"messages": [
{ "role": "user", "content": "Summarize this quarter in two sentences." }
],
"stream": false,
"dex_strategy": "cost"
}| Field | Values | What it does |
|---|---|---|
model | "auto" or a model key | Routes, or pins one model. See the next section. |
messages | at least one message | The whole transcript is classified, joined as role: content lines. Each call is routed on its own. |
stream | true / false | Whether the answer is delivered in chunks. See Streaming below. |
dex_strategy | balanced, cost, intelligence, latency | Cloud Score extension. Overrides the scoring weights for this call; omit it (or send auto) to defer to policies and workspace defaults. |
temperature | number | A non-zero value makes the call ineligible for the response cache (reason sampling). |
max_tokens | integer | Used as the expected output length for cost estimates (500 when absent) and hard-clamped to the model's max_output_tokens. |
response_format | json_object / json_schema | Adds the json_mode capability, turns on output validation with one re-prompt, stands the semantic cache down, and escalates an output redaction to a block. |
tools | list | Passed through to every provider type; an operator can switch tool calls off per provider type (see Tool calls by provider). Tool types other than function route to the OpenAI family only. Provider prompt caching applies to tool requests; the response cache does not (reason tools). |
tool trimming | routing policy | Measured only by default: Cloud Score records which tools it would drop and the estimated saving. Set tool_trim to auto in a routing policy scoped to one agent to send only the relevant tools; it needs at least 10 tools and 1,000 tool tokens, keeps at most 8, and is switched on only after 200 observed requests with a miss rate of 1% or less. Auto holds the full set when trimming would cost more in provider cache than it saves. Savings are estimates. |
tool_choice | none / auto / required / {type:"function",function:{name}} | Forwarded with tools. |
parallel_tool_calls | boolean | Forwarded with tools. |
max_completion_tokens | integer | Same as max_tokens; max_tokens wins when both are sent. |
stop | string or list | Forwarded; not sent to reasoning models, which reject it. |
n | 1 | n > 1 is rejected with HTTP 400, error.code unsupported_parameter. |
metadata | team, application, agent, environment, end_user | Cost attribution tags. Other keys are ignored; values are capped at 120 characters. |
user | string | The OpenAI-standard field; Cloud Score maps it to end_user. |
dex_cache | Cloud Score extension | Accepted by the endpoint alongside dex_strategy. The Playground never sends it. |
| Header | Direction | Meaning |
|---|---|---|
Authorization: Bearer dex_sk_… | request | Required on every call. |
Content-Type: application/json | request | Required. |
X-Dex-Cache-Control: no-cache|no-store | request | Per-request cache directive. no-store: nothing is read or written; no-cache: the lookup is skipped but the answer is still written back. |
X-Dex-team, X-Dex-application, X-Dex-agent, X-Dex-environment, X-Dex-end-user | request | Attribution by header. The body metadata wins over headers, which win over the key's stored defaults. |
Tool calls
Calls return in message.tool_calls, with content null when there is no text, and finish_reason tool_calls. Cloud Score never runs your tools.
AI Security scans tool-call arguments in both directions. A block category blocks the call (403). A redact category is logged and the arguments pass unaltered. Tool definitions are not scanned.
Models whose provider type an operator switched off are skipped; when none is left, or you pinned one, the call fails with HTTP 409 and error.code tools_unsupported.
Route with auto or pin a model
| You send | What Cloud Score does | Failover | Cache |
|---|---|---|---|
model: "auto" | Routes. Enabled models are filtered by capability and context window, priced, checked against policies and budgets, scored on cost, latency, and quality, and the winner is called. | On a hard provider error the failed model is excluded and selection re-runs on the rest, within 3 billed attempts per request. | Exact and semantic lookups both possible when a policy enables caching. |
model: "<model_key>" | Pins. The name becomes model_override; the model_override filter leaves exactly one candidate. Policies, budgets, and guards still apply. | None. A hard provider failure fails the request instead of rerouting. An unknown or disabled name is rejected up front as no_candidates. | Exact lookups only; semantic matching stands down for a pinned model. |
The helper text on Developer says it plainly
Pinning a model disables cross-model failover — a hard provider failure fails the request instead of rerouting. Availability is workspace-level; keys don't carry per-key model restrictions.
To find the names you may pin, call GET /v1/models: a list of the models enabled for your workspace with id, display_name, capabilities, context_window, and pricing as strings. On Developer, the Explicit model mode shows the same set in the select labelled Model (enabled in this workspace, provider connected), each option as {display_name} · {model_key} ({provider_type}), and writes your pick into the snippet as model: "<model_key>". With nothing chosen it pins the first usable model straight away; with none available it reads No usable models — connect a provider first.
Choose a strategy with dex_strategy
dex_strategy carries the same five presets as the native constraints.strategy. It changes scoring only: policy blocks, allow and deny lists, forced models, cost caps, budgets, and guards still apply. A strategy you send is the strongest weight source, above a policy's preferred tier and the workspace scoring weights on Admin › Organization.
| Value | Tile on Developer | Weights | What it does |
|---|---|---|---|
auto | Auto | workspace policies & defaults | Defer entirely to your workspace's routing policies and configured defaults. |
balanced | Balanced | 40% cost · 20% latency · 40% quality | Pin the balanced preset for this request, even where a policy prefers a tier. |
cost | Cost | 70% cost · 10% latency · 20% quality | Prefer the cheapest capable model. |
intelligence | Intelligence | 10% cost · 20% latency · 70% quality | Prefer the highest-quality model. |
latency | Latency | 10% cost · 70% latency · 20% quality | Prefer the fastest model. |
The generated snippet does not write dex_strategy
With OpenAI-compatible selected, the Intelligent routing tiles other than Auto only add a comment to the snippet saying that named strategies are native-API-only, and the snippet still sends model: "auto". The endpoint does accept dex_strategy. To use a strategy from your own code, add the field to the request body yourself.
auto defers, balanced pins. Only an absent strategy (or auto) inherits a policy's preferred tier and the workspace weights; balanced explicitly pins 40 / 20 / 40 over them. See Routing strategies.
Read the dex_routing block
Alongside the standard completion fields, the response carries dex_routing: the same routing and cache information the native envelope puts under routing. The Playground uses the native endpoint only, so it never reads this block.
| Field | Meaning |
|---|---|
model | Model key that answered. |
provider | Provider that served it. |
strategy | The dex_strategy you sent. Present only when you sent one. |
cost_usd | Measured cost at list price. A JSON number in this envelope, not a string. |
cache | status (hit / miss / ineligible / off), source (exact / semantic), reason, and similarity on semantic paths. |
security | input and output verdicts: the action (off, log, redact, block) and the categories matched. |
classifier, task_type, complexity, candidates_considered, attempts, reasons | The classification and the decision summary, as on the native envelope. |
optimization, attribution, budget | The optimization plan summary, the attribution tags that applied, and the budget settlement. |
No savings figures in the compat usage block
The top-level usage block has no saved_* fields, so a cache hit on this endpoint reports what it cost (cost_usd is 0 for a served hit) but not the dollars it avoided. A client reading this block therefore sees only the cache status and source, never the tokens or dollars saved. The saving is still recorded on the request row and shows in Logs and Cost.
The response carries no decision trace. The backend runs the identical pipeline, so open the request in Traces to see its Route board, the Optimization decision card, and the Cache section.
Errors and HTTP codes
Unlike the native endpoint, which answers HTTP 200 and puts the outcome in status, this endpoint uses real HTTP codes. A block is a 403; every other failure is collapsed to a 502, with two exceptions: 400 and 409. The body is {error:{message,type}}.
{
"error": {
"message": "blocked by AI Security",
"type": "security_blocked"
}
}| HTTP | When | error.type | error.message |
|---|---|---|---|
401 | The key is unknown or disabled (same text for both). | — | missing or invalid API key |
403 | A policy blocked the request. | blocked | blocked by policy '<name>', or the policy's own message. |
403 | A budget is exhausted. | blocked | budget exhausted: <scope> <scope_value> at <utilization_pct>% (model_spend) |
403 | AI Security blocked the prompt or the answer. | security_blocked | The profile's blocked reason, else blocked by AI Security. |
502 | No enabled model can serve the request, including an unknown or disabled pinned model. | no_candidates | no enabled model matches the required capabilities and context window or no candidate models remain after constraints, policies, and failover exclusions |
409 | Tools were sent (or tool history) and no tool-capable model remains, or the pinned model's provider type was switched off by the operator (TOOLS_DISABLED_PROVIDER_TYPES) or cannot take a non-function tool type. | no_candidates | code tools_unsupported |
400 | n > 1. | invalid_request_error | code unsupported_parameter |
400 | A tool message does not answer the tool call just before it, or a tool call has no answer. | invalid_request_error | code invalid_tool_history; names the tool_call_id |
502 | Every eligible model failed after retries and failover. | provider_error | <model_key>: <scrubbed provider error> |
502 | The answer failed validation after the one re-prompt. | validation_failed | The validator's reason, for example empty response. |
502 | No time was left on the request deadline for another attempt. | deadline_exceeded | <model_key>: request deadline exhausted before another attempt could start |
422 | The body failed schema validation. | — | The validation messages joined by ; . |
| error.type | Meaning |
|---|---|
no_candidates | No enabled model satisfies the constraints (including an unknown or disabled pinned model). |
blocked | A routing policy or budget blocked the request before execution. |
provider_error | Every eligible model failed after retries and failover. |
validation_failed | The output failed validation; a partial_output preview is included. |
Read error.type before retrying a 502
The native endpoint's HTTP contract gives no_candidates a 409 so that SDK retry policies do not amplify a configuration failure. This endpoint gives it a 502 like a provider outage, so an SDK that retries 5xx responses will retry a request that can never succeed until a model is enabled or the pinned name is fixed. When the message is request failed, no more specific reason was recorded. A tool request whose candidates were all skipped for tool support is the exception: HTTP 409, code tools_unsupported, with x-should-retry: false so the OpenAI and Anthropic SDKs do not retry it.
Streaming
Streaming on this endpoint is buffered: nothing reaches your client until Guard out and Validate have settled, and only then is the settled output flushed. That is different from native SSE, where chunks stream as the model produces them.
| Aspect | This endpoint, stream: true | Native /v1/requests, stream: true |
|---|---|---|
| When text arrives | After Guard out and validation settle, in one flush. | Live as the model produces it, unless the key's security profile enforces output, in which case it is buffered the same way. |
| A failed attempt or a re-prompt | The discarded attempt's text is never shown. | Already-shown chunks are retracted with a reset event. |
| Trace | Not in the response; see Logs. | dex_trace events, one per stage. |
| Tool calls | One chunk carrying every complete call (delta.tool_calls, each with index), after Guard out and validation settle, then the final chunk with finish_reason tool_calls. | Not available: the native endpoint does not take tools. |
| In the Playground | Not used: the Playground sends the native endpoint only. | Streams while Stream the answer is on, under Advanced options. |
Because output is only released once it is final, a response-format request that needs the one billed re-prompt never shows you the invalid first answer.
Try it from your own client
The Playground uses the native endpoint only, so it cannot send this endpoint for you. Try it with the request body from Request shape and a gateway key.
Send the same body from your own client (curl or an SDK).
Post it to
/v1/chat/completionswithAuthorization: Bearer dex_sk_…. With the official OpenAI SDK, point it at the gateway as described above.Read
dex_routingin the response.model,provider,strategy,cost_usd,cache, andsecuritysay what served the call, what it cost, and what the cache and AI Security decided.Open the request in
Traces.The response carries no decision trace; the request drawer in Logs shows its stations.
A cost-first message
- What you do
Send the request body above, with
dex_strategyset tocost.- What Cloud Score does
Classifies the transcript, scores the enabled models with 70 / 10 / 20 weights, and calls the winner.
- What you see
A response whose
dex_routingreadsstrategycost, themodelandproviderthat answered,cost_usd, and acacheblock whosestatusandreasonsay whether the answer could be cached.
A key bound to a profile that redacts PII
- What you do
Send
My SSN is 123-45-6789, draft a reply.- What Cloud Score does
Guard inrewrites the prompt before any model sees it and recordsredact · pii; the reply comes back withdex_routing.securitycarrying that verdict.- What you see
A normal reply whose
dex_routing.securityinput verdict isredactwith the categorypii. Had the profile been set to block, the call would have been an HTTP 403 whose error message is the profile's blocked reason.
Good to know
Native-only controls
This endpoint has no equivalent of the native constraints.quality, max_cost_usd, max_latency_ms, required_capabilities, or policy_ids. Use the native endpoint when you need a cost cap, a latency bound, a capability filter, or sandbox policy selection.
Every message is its own request
The transcript is resent in full on every turn and classified as a whole, and each reply is routed, traced, and billed on its own. The cache fingerprint covers the whole transcript, so cache hits are mostly identical single-turn sends. The model that answered the previous turn of the same conversation gets a small affinity bonus at selection; it is a tie-breaker, never an override.
Attribute cost from the same call
Add metadata with any of team, application, agent, environment, end_user, or set the OpenAI-standard user field, which Cloud Score maps to end_user. The tags come back in dex_routing.attribution and let Cost group spend by them. The Developer snippet does not include metadata; you add it to the call.
Cache eligibility is strict
A call with tools, a non-zero temperature, a key with body storage off, or a prompt that Guard in redacted is never served from or written to the response cache. The reason is in dex_routing.cache.reason. See Caching and deduplication.
Tool calls by provider
| Provider type | tool_choice | parallel_tool_calls: false |
|---|---|---|
| openai, azure, grok, openrouter, together | Sent as given. | Honoured. |
anthropic | required becomes any; a named function becomes tool. On claude-fable-5-1, claude-opus-5-5 and claude-sonnet-5-5 a forced choice is sent as auto, because those models refuse forced tool use. | Honoured. |
| gemini, vertex | auto, required or none; a named function is the only allowed function. | Not supported, ignored. |
bedrock | auto or required. A named function is forced on Claude and Nova and means any tool elsewhere. none is sent as auto when the conversation already holds tool calls. | Not supported, ignored. |
An operator can switch tool calls off per provider type; tool requests that only those providers could serve then get HTTP 409 tools_unsupported.
Attempts and deadline
A request gets at most 3 billed provider calls: failover switches, the validation re-prompt, and an output-budget rollback all count. Same-model retries on rate limits, transport failures, and timeouts do not. A request that runs out of deadline before another attempt can start fails with deadline_exceeded and does not fail over.
Related
- Attribute cost to teams and appsTag every request with team, application, agent, environment, and end_user so Usage & cost can break spend down by them as reported tags, budgets can cap a team or an app, and alert rules can watch them.
- SDK snippetsAdmin › Developer generates a copy-paste client for Python, TypeScript, or Go, against the native API or the OpenAI SDK, with a routing strategy or a pinned model baked in.
- TroubleshootingThe messages Cloud Score puts in front of you, grouped by the page that shows them, with what caused each one and what to do next.
- Statuses and badgesEvery status word and badge Cloud Score shows, what each one means, and the page it appears on.
- Investigate a failed, blocked, or expensive requestTrace one request from its row in Observability to the decision card, its savings receipt and a Playground reproduction, then read what its error message means.
- Protect sensitive data in prompts and answersBuild an AI Security profile that redacts card numbers and blocks a confidential codename, attach it to a key, prove it in the Playground, and audit every verdict in Logs.