Skip to content
Cloud Score Docs

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.

WhereAdminDeveloperNeeds a connected providerNeeds an enabled modelNeeds a gateway keyOpen in Cloud Score
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

The two rows of the Endpoints table on Admin › Developer that this page covers.
MethodPathPurpose
POST/v1/chat/completionsOpenAI-compatible. model: "auto" routes; a concrete name pins.
GET/v1/modelsModels 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.

  1. Click Developer under Admin in the sidebar.

    The Developer page opens. Its Quick start card lists the three things you need: Create an API key, Export it in your shell, Copy the snippet below and run it.

  2. Click Admin → API keys, create a key, and copy its dex_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.

  3. Export the key in your shell.

    shell
    export DEX_API_KEY="dex_sk_…"
  4. Under Integration snippet, click OpenAI-compatible, then Python, TypeScript, or Go.

    The code block header reads Python · OpenAI-compatible (or your language). The blurb switches to the OpenAI SDK wording quoted above.

  5. Leave Intelligent routing and the Auto tile selected.

    In this mode the snippet always sends model: "auto", so the workspace routes every call. Pinning a model is the Explicit model mode, covered below.

  6. Set Base URL if the gateway is not on http://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.

  7. Click Copy, paste the snippet into your project, and run it.

    The button reads Copied for 1.5 seconds. A blocked request surfaces as an HTTP 403 and any other failure as a 502, raised by the SDK as APIStatusError or OpenAI.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.

POST /v1/chat/completions
{
  "model": "auto",
  "messages": [
    { "role": "user", "content": "Summarize this quarter in two sentences." }
  ],
  "stream": false,
  "dex_strategy": "cost"
}
FieldValuesWhat it does
model"auto" or a model keyRoutes, or pins one model. See the next section.
messagesat least one messageThe whole transcript is classified, joined as role: content lines. Each call is routed on its own.
streamtrue / falseWhether the answer is delivered in chunks. See Streaming below.
dex_strategybalanced, cost, intelligence, latencyCloud Score extension. Overrides the scoring weights for this call; omit it (or send auto) to defer to policies and workspace defaults.
temperaturenumberA non-zero value makes the call ineligible for the response cache (reason sampling).
max_tokensintegerUsed as the expected output length for cost estimates (500 when absent) and hard-clamped to the model's max_output_tokens.
response_formatjson_object / json_schemaAdds 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.
toolslistPassed 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 trimmingrouting policyMeasured 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_choicenone / auto / required / {type:"function",function:{name}}Forwarded with tools.
parallel_tool_callsbooleanForwarded with tools.
max_completion_tokensintegerSame as max_tokens; max_tokens wins when both are sent.
stopstring or listForwarded; not sent to reasoning models, which reject it.
n1n > 1 is rejected with HTTP 400, error.code unsupported_parameter.
metadatateam, application, agent, environment, end_userCost attribution tags. Other keys are ignored; values are capped at 120 characters.
userstringThe OpenAI-standard field; Cloud Score maps it to end_user.
dex_cacheCloud Score extensionAccepted by the endpoint alongside dex_strategy. The Playground never sends it.
HeaderDirectionMeaning
Authorization: Bearer dex_sk_…requestRequired on every call.
Content-Type: application/jsonrequestRequired.
X-Dex-Cache-Control: no-cache|no-storerequestPer-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-userrequestAttribution 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 sendWhat Cloud Score doesFailoverCache
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.

ValueTile on DeveloperWeightsWhat it does
autoAutoworkspace policies & defaultsDefer entirely to your workspace's routing policies and configured defaults.
balancedBalanced40% cost · 20% latency · 40% qualityPin the balanced preset for this request, even where a policy prefers a tier.
costCost70% cost · 10% latency · 20% qualityPrefer the cheapest capable model.
intelligenceIntelligence10% cost · 20% latency · 70% qualityPrefer the highest-quality model.
latencyLatency10% cost · 70% latency · 20% qualityPrefer 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.

FieldMeaning
modelModel key that answered.
providerProvider that served it.
strategyThe dex_strategy you sent. Present only when you sent one.
cost_usdMeasured cost at list price. A JSON number in this envelope, not a string.
cachestatus (hit / miss / ineligible / off), source (exact / semantic), reason, and similarity on semantic paths.
securityinput and output verdicts: the action (off, log, redact, block) and the categories matched.
classifier, task_type, complexity, candidates_considered, attempts, reasonsThe classification and the decision summary, as on the native envelope.
optimization, attribution, budgetThe 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}}.

403 Forbidden
{
  "error": {
    "message": "blocked by AI Security",
    "type": "security_blocked"
  }
}
HTTPWhenerror.typeerror.message
401The key is unknown or disabled (same text for both).—missing or invalid API key
403A policy blocked the request.blockedblocked by policy '<name>', or the policy's own message.
403A budget is exhausted.blockedbudget exhausted: <scope> <scope_value> at <utilization_pct>% (model_spend)
403AI Security blocked the prompt or the answer.security_blockedThe profile's blocked reason, else blocked by AI Security.
502No enabled model can serve the request, including an unknown or disabled pinned model.no_candidatesno enabled model matches the required capabilities and context window or no candidate models remain after constraints, policies, and failover exclusions
409Tools 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_candidatescode tools_unsupported
400n > 1.invalid_request_errorcode unsupported_parameter
400A tool message does not answer the tool call just before it, or a tool call has no answer.invalid_request_errorcode invalid_tool_history; names the tool_call_id
502Every eligible model failed after retries and failover.provider_error<model_key>: <scrubbed provider error>
502The answer failed validation after the one re-prompt.validation_failedThe validator's reason, for example empty response.
502No time was left on the request deadline for another attempt.deadline_exceeded<model_key>: request deadline exhausted before another attempt could start
422The body failed schema validation.—The validation messages joined by ; .
The error.type table on Admin › Developer, verbatim.
error.typeMeaning
no_candidatesNo enabled model satisfies the constraints (including an unknown or disabled pinned model).
blockedA routing policy or budget blocked the request before execution.
provider_errorEvery eligible model failed after retries and failover.
validation_failedThe 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.

AspectThis endpoint, stream: trueNative /v1/requests, stream: true
When text arrivesAfter 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-promptThe discarded attempt's text is never shown.Already-shown chunks are retracted with a reset event.
TraceNot in the response; see Logs.dex_trace events, one per stage.
Tool callsOne 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 PlaygroundNot 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.

  1. Send the same body from your own client (curl or an SDK).

    Post it to /v1/chat/completions with Authorization: Bearer dex_sk_…. With the official OpenAI SDK, point it at the gateway as described above.

  2. Read dex_routing in the response.

    model, provider, strategy, cost_usd, cache, and security say what served the call, what it cost, and what the cache and AI Security decided.

  3. 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_strategy set to cost.

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_routing reads strategy cost, the model and provider that answered, cost_usd, and a cache block whose status and reason say 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 in rewrites the prompt before any model sees it and records redact · pii; the reply comes back with dex_routing.security carrying that verdict.

What you see

A normal reply whose dex_routing.security input verdict is redact with the category pii. 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 typetool_choiceparallel_tool_calls: false
openai, azure, grok, openrouter, togetherSent as given.Honoured.
anthropicrequired 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, vertexauto, required or none; a named function is the only allowed function.Not supported, ignored.
bedrockauto 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