Skip to main content
POST /v1/chat/completions Creates a chat completion. This is the primary endpoint for generating AI responses. Requests are routed to the appropriate government cloud provider based on the model you specify. Supports text, multi-turn conversations, tool use, and image input (vision).

Request

Headers

Body Parameters

Message Object

Response

Non-Streaming

Streaming

Set stream: true to receive the response as Server-Sent Events (SSE). Streaming is real and incremental — chunks are relayed as the model generates them, so tokens arrive over time (first-token latency reflects the model, not the full completion). Each event is a JSON chunk prefixed with data: , ending with data: [DONE].
Set stream_options: {"include_usage": true} to receive token usage in the final chunk. Streaming requests are not bound by the 5-minute non-streaming ceiling; they run up to ~15 minutes (see Request Timeout), which lets long reasoning turns complete.

Finish Reasons

Reasoning

Some models support extended thinking before producing the final response. The model spends additional output tokens reasoning through the problem internally, then returns the visible answer. This generally improves accuracy on harder tasks at the cost of latency and tokens. Pass reasoning_effort on the request to control it. Reasoning is currently supported on all Claude models (Bedrock and Vertex), GPT-5.1 (Azure), GPT-OSS-120B and GPT-5.4 (Bedrock), and Gemini 3.5 Flash (Vertex). Other models silently ignore the parameter.

Effort levels

On Gemini 3.5 Flash these levels map to Google’s native thinking_level (noneminimal, lowlow, mediummedium, highhigh). Gemini 3.5 thinking cannot be fully turned off, so none means minimal thinking, not zero — the model may still think briefly on hard prompts. xhigh is not a Gemini tier; it is clamped to high. If you omit reasoning_effort, the gateway applies the per-model default from the catalog. Today that is medium on Claude, GPT-OSS-120B, GPT-5.4, and Gemini 3.5 Flash, and high on GPT-5.1. To explicitly disable reasoning on a capable model, send "reasoning_effort": "none".

Token budget

The thinking budget counts against max_tokens. On Claude, the gateway clamps the budget to at most max_tokens - 1024 so room remains for the visible answer, with a floor of 1,024 thinking tokens. If you do not set max_tokens, the gateway sets it to budget + 4096 so the request always has headroom. Setting max_tokens very low will reduce the effective thinking budget. GPT-5.1, GPT-OSS-120B, GPT-5.4, and Gemini 3.5 Flash manage their own reasoning internally from reasoning_effort; the gateway does not clamp their thinking budget against max_tokens the way it does on Claude.

Sampling parameters

On Claude (Bedrock and Vertex), GPT-5.1 (Azure), and GPT-5.4 (Bedrock), temperature and top_p are not compatible with reasoning: when reasoning_effort is a non-none value, both are stripped before forwarding upstream to avoid 400 errors. GPT-OSS-120B (Bedrock) and Gemini 3.5 Flash (Vertex) accept temperature and top_p alongside reasoning, so they are preserved.

Tool choice

When reasoning is enabled on a Claude model, a forced tool_choice (required or a specific tool) is coerced to auto. The model needs latitude to think before deciding whether to call a tool. Tool definitions still pass through unchanged. GPT-5.1, GPT-OSS-120B, and GPT-5.4 have no equivalent restriction. On Gemini models (reasoning or not), tool definitions pass through but a forced tool_choice is not translated to Vertex AI — the model always selects tools automatically.

Reasoning token usage

When the model used reasoning, the OpenAI-shape usage object includes a completion_tokens_details.reasoning_tokens count. Reasoning tokens are already included in completion_tokens; the field is surfaced separately so you can attribute spend.
This field is populated for GPT-5.1, GPT-5.4, and Gemini 3.5 Flash. On Claude and GPT-OSS-120B the upstream does not expose a separate reasoning token count, so the field is omitted; reasoning tokens are still present, just rolled into completion_tokens.

Example

Tool Use

Pass a tools array to let the model call functions. The model will respond with tool_calls when it wants to use a tool, and you send back the result in a tool message. Tool use is supported on all available Claude, Gemini, and GPT models. See Reasoning > Tool choice for how tool_choice interacts with reasoning_effort on Claude.

Tool Definition

Function name must match ^[a-zA-Z0-9_-]{1,64}$ (ASCII letters, digits, underscore, hyphen; 1 to 64 characters), matching the OpenAI and Anthropic tool name specs. Names outside this pattern return 400 invalid_request_error. description is limited to 65,536 characters.

Tool Call Response

When the model calls a tool, the response includes tool_calls instead of (or alongside) content:

Sending Tool Results

Include the tool call result in a follow-up message with role: "tool":

Rejected Tool Schemas

The gateway rejects tool definitions whose parameter schemas include property names that clearly describe an outbound destination. These schemas are the shape of a data exfiltration tool, and accepting them at the gateway would be careless regardless of what the caller intends to do with the result. Rejected property names (case insensitive):
  • Destination names: destination, destination_url, dest_url, dst_url
  • Webhook names: webhook, webhook_url, webhooks
  • Callback names: callback, callback_url
  • Send and forward names: forward_to, forward_url, send_to, post_to, push_to
  • Target names: target_url, target_host
  • Named sinks: upload_url, ingest_url, notification_url, notify_url, report_url, sink_url
  • Obvious intent: exfil_url, exfiltrate
The check walks the full JSON Schema tree, so hiding a denied name inside a nested property, an array items schema, a $defs entry, or a oneOf branch will not bypass it. Ambiguous names that can legitimately read as well as write are not rejected. url, uri, endpoint, host, and hostname all pass at this layer. A database connection tool with host and port, or a tool that reads a record from an internal API by url, continues to work. The runtime check described in the next section handles what actually appears in the arguments. A rejected request returns 400 invalid_request_error and identifies the offending tool and parameter:
If you have a real business case for a parameter that matches a rejected name, contact us and we can work through the exception together.

Tool Call Governance Metadata

When a model returns tool calls, the gateway scans each arguments payload for outbound destinations (URLs with a scheme like https://, ftp://, s3://, data:, mailto:, and raw IPv4 addresses). When any are found, the response includes an advisory field called x_consus_governance alongside the standard OpenAI body. The tool call itself is not modified. You still receive the real arguments so your application can run. Response shape when destinations are detected:
When no destinations are found, the field is absent from the response. This is an advisory signal. We do not block or redact the tool call. The purpose is to give your application something structured to act on before you execute a tool call whose destination came from the model output. The typical handling pattern is: check for the field, look up each destination against whatever allowlist or policy your application runs under, and surface it to a human or your policy engine when it looks unfamiliar. Streaming responses carry the same field in the final SSE chunk (the chunk that includes finish_reason). Clients that already parse the final chunk for usage can pick up x_consus_governance from the same place.

Examples

Basic Completion

Streaming

With System Prompt and Parameters

Multi-Turn Conversation

Image Input (Vision)

All current Claude and Gemini models accept image input, as do the Azure GPT models (GPT-5.1, GPT-4.1) and GPT-5.4 (Bedrock). GPT-OSS-120B is text-only and does not accept image input. See the models page for the live list. Supported formats: jpeg, png, gif, webp

How images are sent

To include an image, set content to an array instead of a plain string. Each element is a content part with a type field, either "text" or "image_url".
The image_url field name is inherited from OpenAI’s API format. Despite the name, you do not pass a URL. You pass the raw image bytes encoded as a base64 data URI. External URLs (https://...) are rejected with 400 to prevent data exfiltration.
A base64 data URI looks like this:
You encode the raw file bytes to base64, prefix with data:<mime-type>;base64,, and put the whole string in the url field.

Request Format

  • content can contain any number of text and image_url parts in any order
  • Images are only valid in user messages. system and assistant messages must use a plain string for content.
  • Multiple images per message are supported (up to 20)

Size Limits

Requests exceeding these limits are rejected with 400.

Integrations handle this for you

If you’re using an integration like OpenCode or Cline, you don’t need to do any of this manually. Those tools encode images automatically when you paste a screenshot. Just paste and send. The base64 encoding and data URI formatting is handled behind the scenes. If you’re calling the API directly, read on.

Complete Examples

bash (curl)
Python

Document Input (PDF)

All Claude and Gemini models support PDF document inputs. GPT models do not — neither the Azure GPT models (GPT-5.1, GPT-4.1) nor GPT-OSS-120B and GPT-5.4 on Bedrock. Supported file types: application/pdf

How documents are sent

To include a PDF, add a content part with "type": "file" to the content array. The file_data field must be a base64 data URI. External URLs are rejected for the same no-egress reason as images.
  • Documents are only valid in user messages. system and assistant messages must use plain strings.
  • filename is metadata passed to the model; it is treated as untrusted input and sanitized before use
  • Text, images, and files can be mixed in a single message’s content array

Size Limits

The combined image + file budget is shared at 4.5 MB per message, enforced by the gateway’s request payload limit.

curl example

Python example