Azure OpenAI

Azure OpenAI API reference

The /azure/openai surface: both the deployment-based contract with its required api-version and the newer /openai/v1 one, plus the content filter that has no counterpart anywhere else.

Azure OpenAI · Endpoints

Azure serves the same models as OpenAI through a different front door. Most of the differences are in the routing rather than the payloads — the exception being content filtering, which Azure adds to every response. Two surfaces are served under the /azure/openai prefix.

MethodPathNotes
POST…/deployments/{deployment}/chat/completionsFull chat.completion object, SSE streaming with [DONE], tool calls
POST…/deployments/{deployment}/embeddingsDeterministic unit vectors, correct dimension per model
POST/azure/openai/v1/chat/completionsThe newer surface: no api-version, no deployments
POST/azure/openai/v1/embeddingsSame handlers, addressed by model
GET/azure/openai/models?api-version=Model catalog, outside the deployment path

The classic surface addresses a deployment rather than a model and requires api-version on every call. The v1 surface drops both:

# classic
/azure/openai/deployments/{deployment}/chat/completions?api-version=2024-10-21

# v1
/azure/openai/v1/chat/completions
Azure puts the resource name in the hostname ({resource}.openai.azure.com) and keeps /openai in the path. A mock cannot hand out subdomains, but it does not need to — the SDK takes an arbitrary URL, so the resource simply becomes part of the prefix. Both surfaces run the same handlers, so the same request returns the same body on either.

Azure · Authentication

Azure takes the key in an api-key header rather than a bearer token — the single most common reason an OpenAI-shaped client fails against it. Authorization: Bearer is accepted too, as Entra ID callers use it.

import { AzureOpenAI } from "openai";

const client = new AzureOpenAI({
  baseURL: "http://localhost:3000/azure/openai",
  apiKey: "sk-mock-key-01",
  apiVersion: "2024-10-21",
});

Use baseURL rather than endpoint: the SDK reads OPENAI_BASE_URL from the environment and then refuses to combine it with endpoint, so a stray env var breaks the client before it sends anything. The v1 surface needs no Azure-specific client at all — a plain OpenAI pointed one level deeper at /azure/openai/v1.

The two failures answer differently, and that is Azure's own doing: a missing key is rejected by the API gateway, which knows nothing about the service's error envelope, while an invalid one gets the wrapped shape.

// missing credential — no `error` wrapper at all
{ "statusCode": 401, "message": "Access denied due to missing subscription key…" }

// invalid credential — the service's envelope
{ "error": { "code": "401", "message": "Access denied due to invalid subscription key…" } }

Note that Azure's code is a symbolic string (DeploymentNotFound, BadRequest) where OpenAI uses a snake_case slug or null.

Azure · Deployments

A deployment is a named alias a customer creates in their own Azure resource, pointing at a model. A mock has no resource and therefore no deployments, so any name is accepted — the way every provider here accepts any model id.

What cannot be dropped is the 404. DeploymentNotFound is the error Azure users hit most, and the one their code most needs to handle, so it is driven by the name itself: anything starting with missing- returns the real thing.

{
  "error": {
    "code": "DeploymentNotFound",
    "message": "The API deployment for this resource does not exist. If you created the deployment within the last 5 minutes, please wait a moment and try again."
  }
}
The deployment routes the call; the model echoed back is the one the client asked for, since the two need not agree — the SDK only uses model as the deployment segment when no fixed deployment is configured. The reserved prefix belongs to the deployment path only: on the v1 surface there is no deployment to be missing, and missing-one is just a model name.

Azure · Content filtering

The most Azure-specific thing about the service, and the reason an OpenAI-shaped client can meet a response it did not expect. Every chat completion carries the filter's verdict — on the prompt at the top level, on each choice individually.

{
  "choices": [{
    "message": { "content": "Echo: hello" },
    "finish_reason": "stop",
    "content_filter_results": {
      "hate":      { "filtered": false, "severity": "safe" },
      "self_harm": { "filtered": false, "severity": "safe" },
      "sexual":    { "filtered": false, "severity": "safe" },
      "violence":  { "filtered": false, "severity": "safe" }
    }
  }],
  "prompt_filter_results": [{
    "prompt_index": 0,
    "content_filter_results": { /* the four above, plus binary detectors */
      "jailbreak": { "filtered": false, "detected": false } }
  }]
}

To exercise the failure paths, pin the verdict with x-llm-mock-content-filter, as <target> or <target>:<category>:<severity>:

ValueResult
prompt400 with code: "content_filter", and the verdict nested in innererror
completion200 with content: null and finish_reason: "content_filter"
unavailable200, with an error object where the verdict would be — the documented "filter did not run" case

Categories are hate, self_harm, sexual, violence and jailbreak; severities safe, low, medium, high. So completion:violence:high filters the reply and says which category did it.

Two quirks are reproduced deliberately, because code written against OpenAI trips on both. A streamed response opens with a chunk carrying no choices at all, just the prompt's verdict — a client reaching straight for chunk.choices[0] breaks there. And the blocked-prompt error nests its verdict under content_filter_result, singular, against the plural used everywhere else.

None of this provider's shapes could be checked against a live Azure resource. Its routes come from observing what AzureOpenAI puts on the wire, and its payloads from Microsoft's documentation and published examples — but they are not observed responses. The empty first streaming chunk in particular is reconstructed from reported behaviour rather than seen.

Other providers

Same server, same key set, one page each.

ProviderPrefixReference
OpenAI/openai/v1Read →
Anthropic/anthropicRead →
Gemini (AI Studio)/geminiRead →
Gemini Enterprise/gemini-enterpriseRead →