> For the complete documentation index, see [llms.txt](https://docs.otari.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.otari.ai/otari/for-integrators/api-reference/chat.md).

# Chat

## Chat Completions

> OpenAI-compatible chat completions endpoint.\
> \
> Supports both streaming and non-streaming responses.\
> Handles reasoning content from otari providers.\
> \
> Authentication modes:\
> \- Master key + user field: Use specified user (must exist)\
> \- API key + user field: Use specified user (must exist)\
> \- API key without user field: Use the shared "default" user

```json
{"openapi":"3.1.0","info":{"title":"otari","version":"0.33.0"},"security":[{"ApiKeyAuth":[]},{"XApiKeyAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"description":"Enter your API key here (sent as Otari-Key header).","in":"header","name":"Otari-Key","type":"apiKey"},"XApiKeyAuth":{"description":"Anthropic-native clients send credentials here (no Bearer prefix).","in":"header","name":"x-api-key","type":"apiKey"}},"schemas":{"ChatCompletionRequest":{"description":"OpenAI-compatible chat completion request.\n\nThe completion-param fields are derived from any-llm's ``CompletionParams``\n(see ``_schema_derive``) so the schema cannot silently drop a param any-llm\nforwards. Fields below either tighten a derived field (``messages``,\n``response_format``), declare an OpenAI wire param ``CompletionParams`` does\nnot model (``service_tier``, forwarded as an any-llm ``**kwargs`` param), add\ngateway-internal behavior (``mcp_servers``, ``mcp_server_ids``,\n``guardrails``, ``tools_header``, ``max_tool_iterations``) that is stripped\nbefore the request is forwarded upstream, or restate a derived field\nunchanged to document it (``max_completion_tokens``), which is only worth\ndoing where the wire contract is not guessable from the field itself.","properties":{"frequency_penalty":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Frequency Penalty"},"guardrails":{"anyOf":[{"items":{"$ref":"#/components/schemas/GuardrailConfig"},"maxItems":8,"type":"array"},{"type":"null"}],"title":"Guardrails"},"logit_bias":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Logit Bias"},"logprobs":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Logprobs"},"max_completion_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Upper bound on generated tokens. OpenAI's current name for the cap `max_tokens` used to carry; either field is accepted, and this one wins when a request sends both.","title":"Max Completion Tokens"},"max_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Tokens"},"max_tool_iterations":{"anyOf":[{"maximum":25,"minimum":1,"type":"integer"},{"type":"null"}],"title":"Max Tool Iterations"},"mcp_server_ids":{"anyOf":[{"items":{"format":"uuid","type":"string"},"maxItems":50,"type":"array"},{"type":"null"}],"title":"Mcp Server Ids"},"mcp_servers":{"anyOf":[{"items":{"$ref":"#/components/schemas/McpServerConfig"},"type":"array"},{"type":"null"}],"title":"Mcp Servers"},"messages":{"items":{"additionalProperties":true,"type":"object"},"minItems":1,"title":"Messages","type":"array"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"description":"Tags for cost attribution, recorded on the request's usage rows and filterable in the usage API: up to 16 string pairs, keys up to 64 characters and values up to 512. A null value is ignored. LiteLLM's nested `spend_logs_metadata` object is also read, and wins over a flat key of the same name; it is never forwarded to the provider.","title":"Metadata"},"model":{"title":"Model","type":"string"},"n":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"N"},"parallel_tool_calls":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Parallel Tool Calls"},"presence_penalty":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Presence Penalty"},"prompt_cache_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prompt Cache Key"},"reasoning_effort":{"anyOf":[{"enum":["none","minimal","low","medium","high","xhigh","max","auto"],"type":"string"},{"type":"null"}],"default":"auto","title":"Reasoning Effort"},"response_format":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response Format"},"safety_identifier":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A stable identifier for the end user behind the request, used as the tag the provider's end-user identifier is derived from. Wins over `user` for that, and is never sent upstream. The dashboard's Playground ignores it.","title":"Safety Identifier"},"seed":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Seed"},"service_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Service Tier"},"session_label":{"anyOf":[{"maxLength":255,"type":"string"},{"type":"null"}],"description":"Optional caller-supplied label for a run, experiment or conversation, which names the session the request's trace is grouped under. Stripped before the request is forwarded upstream to the provider.","title":"Session Label"},"stop":{"anyOf":[{"type":"string"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Stop"},"stream":{"default":false,"title":"Stream","type":"boolean"},"stream_options":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Stream Options"},"temperature":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Temperature"},"tool_choice":{"anyOf":[{"type":"string"},{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Tool Choice"},"tools":{"anyOf":[{"items":{"anyOf":[{"additionalProperties":true,"type":"object"},{}]},"type":"array"},{"type":"null"}],"title":"Tools"},"tools_header":{"anyOf":[{"maxLength":4000,"type":"string"},{"type":"null"}],"description":"Optional override for the lead-in that the gateway prepends before the per-tool hint block in the system message. Useful for expressing global tool-selection policy (e.g. 'prefer MCP tools over code_execution'). Falls back to OTARI_TOOLS_HEADER env, then to the built-in default.","title":"Tools Header"},"top_logprobs":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Top Logprobs"},"top_p":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Top P"},"user":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User"}},"required":["model","messages"],"title":"ChatCompletionRequest","type":"object"},"GuardrailConfig":{"description":"A single guardrail check the caller wants the gateway to enforce.\n\nURL safety: when ``url`` is supplied it is validated by\n:func:`gateway.services.guardrails.run_input_guardrails` (not here at parse\ntime — the check does a DNS lookup that must be awaited) with the same\nSSRF guard used for MCP server URLs (loopback allowed by default for\nsame-host sidecars; gated by ``OTARI_MCP_ALLOW_LOOPBACK`` /\n``OTARI_MCP_ALLOW_PRIVATE_HOSTS``). Most deployments omit ``url`` and rely\non the operator-set ``OTARI_GUARDRAILS_URL`` instead.","properties":{"mode":{"default":"monitor","enum":["block","monitor"],"title":"Mode","type":"string"},"on":{"items":{"enum":["input","output"],"type":"string"},"title":"On","type":"array"},"on_unavailable":{"default":"block","enum":["block","monitor"],"title":"On Unavailable","type":"string"},"profile":{"maxLength":128,"minLength":1,"title":"Profile","type":"string"},"url":{"anyOf":[{"minLength":1,"type":"string"},{"type":"null"}],"title":"Url"},"validate_kwargs":{"additionalProperties":true,"title":"Validate Kwargs","type":"object"}},"required":["profile"],"title":"GuardrailConfig","type":"object"},"McpServerConfig":{"description":"Inline MCP server configuration accepted by generation requests.\n\nStreamable HTTP transport. The `url` must be reachable from the gateway process.\n\nURL safety (SSRF guard against private/link-local/reserved IP ranges, plus\nrejecting plain ``http://`` when ``authorization_token`` is set) is\nenforced by :func:`gateway.services.url_safety.validate_mcp_url`, called\nfrom the async request pipeline (``prepare_gateway_tools``) rather than\nhere at parse time: the safety check does a DNS lookup, which must be\nawaited so it can't block the event loop, and Pydantic validators run\nsynchronously during request-body parsing.","properties":{"allowed_tools":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Tools"},"authorization_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization Token"},"name":{"maxLength":128,"minLength":1,"title":"Name","type":"string"},"purpose_hint":{"anyOf":[{"maxLength":2000,"type":"string"},{"type":"null"}],"title":"Purpose Hint"},"url":{"minLength":1,"title":"Url","type":"string"}},"required":["name","url"],"title":"McpServerConfig","type":"object"},"HTTPValidationError":{"properties":{"code":{"const":"invalid_request","description":"Stable error code, also sent as the Otari-Error-Code header.","title":"Code","type":"string"},"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"title":"Detail","type":"array"}},"title":"HTTPValidationError","type":"object"},"ValidationError":{"properties":{"ctx":{"title":"Context","type":"object"},"input":{"title":"Input"},"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"title":"Location","type":"array"},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}},"required":["loc","msg","type"],"title":"ValidationError","type":"object"}}},"paths":{"/api/v1/chat/completions":{"post":{"description":"OpenAI-compatible chat completions endpoint.\n\nSupports both streaming and non-streaming responses.\nHandles reasoning content from otari providers.\n\nAuthentication modes:\n- Master key + user field: Use specified user (must exist)\n- API key + user field: Use specified user (must exist)\n- API key without user field: Use the shared \"default\" user","operationId":"chat-chat_completions","parameters":[{"description":"A unique value, such as a UUID, that makes a non-streaming request safe to retry. A retry with the same key and body returns the original response, request ID and cost without calling the provider or billing again. A retry while the original is still running is answered 409 with Retry-After. Reusing a key for a different body is refused with 422. Ignored for streaming requests and on a deployment without OTARI_SECRET_KEY, which encrypts the stored response.","in":"header","name":"Idempotency-Key","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A unique value, such as a UUID, that makes a non-streaming request safe to retry. A retry with the same key and body returns the original response, request ID and cost without calling the provider or billing again. A retry while the original is still running is answered 409 with Retry-After. Reusing a key for a different body is refused with 422. Ignored for streaming requests and on a deployment without OTARI_SECRET_KEY, which encrypts the stored response.","title":"Idempotency-Key"}},{"description":"Tags for cost attribution, as comma-separated `key=value` pairs (`agent=bug-fix,run=1234`), for a client that cannot set the body's `metadata`. Keys and values are percent-decoded, so either may carry an encoded `,` or `=`, and a non-ASCII character must be encoded. Whitespace around a pair is ignored, and a repeated header is read as one list. The bounds are the body's: up to 16 pairs, keys up to 64 characters and values up to 512, counted after merging with the body's tags, which win on the same key. Never forwarded to the provider.","in":"header","name":"Otari-Tags","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Tags for cost attribution, as comma-separated `key=value` pairs (`agent=bug-fix,run=1234`), for a client that cannot set the body's `metadata`. Keys and values are percent-decoded, so either may carry an encoded `,` or `=`, and a non-ASCII character must be encoded. Whitespace around a pair is ignored, and a repeated header is read as one list. The bounds are the body's: up to 16 pairs, keys up to 64 characters and values up to 512, counted after merging with the body's tags, which win on the same key. Never forwarded to the provider.","title":"Otari-Tags"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatCompletionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{}}},"description":"Successful Response"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}},"description":"Validation Error"}},"summary":"Chat Completions","tags":["chat"]}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.otari.ai/otari/for-integrators/api-reference/chat.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
