knowledge — see Grounding the answer. Web search is off too unless knowledge includes web — see Web search.
Before you start
- API key: every request needs a Bearer token. Admins create API keys in the workspace settings under the API Keys section.
- Key permissions: the key needs Workspaces: Read on the space in the URL, otherwise the request is a
403. For answers grounded in the knowledge base it also needs KB assets: Read (uploaded files) and Contents: Read (authored documents): without them retrieval finds nothing, and the answer comes back without grounding rather than as an error. A key never reaches further than the role of the admin who created it. - Keep the key server-side: the endpoint accepts requests from browsers, so a key shipped in client-side code can be used by anyone who reads it.
- Model ids: ask
GET .../openai/modelsfor the ids this space accepts. They are nuwacom catalog keys (for exampleclaude-sonnet-5-5oropenai-gpt-6.1-sol), not OpenAI model names.
Base URL
{customer-tenant} with your workspace’s tenant name and {spaceId} with the space the request should run against. That URL is what an SDK takes as its base_url; the SDK appends /chat/completions and /models itself.
Endpoints
GET /models returns the public model catalog narrowed to what this space’s administrators have enabled in Settings, and GET /models/{model} answers one entry of that same list — which is also the set POST /chat/completions gates on, so the three cannot disagree.
Model capabilities
Each model entry carries acapabilities object, a nuwacom extension the OpenAI SDKs keep as an extra field (model.model_extra["capabilities"] in Python). It is read off the same rules POST /chat/completions enforces, so what it allows is what a request may send:
This API serves Chat Completions and Models only. Other OpenAI endpoints — the Responses API, Embeddings, Files — answer with an OpenAI-shaped
404 naming what is served, rather than a bare not-found.
Quickstart
- OpenAI Python SDK
- Vercel AI SDK
- Pydantic AI
- cURL
stream=True; the chunk that carries usage has no choices, so skip it:Coding agents that execute their own tools on your machine — Cursor’s or GitHub Copilot’s agent mode, Claude Code, Codex — cannot use this endpoint: it refuses client-side tools, because the request already runs a server-side agent.
Supported parameters
The endpoint accepts OpenAI’s Chat Completions body with the following exceptions. Anything marked Not supported is answered with a400 whose message says why, rather than being silently dropped.
Unknown fields are rejected rather than ignored, so a
200 means every field you sent was honoured.
Sampling parameters are applied where the backend serving the model takes them. The OpenAI reasoning families (o-series, GPT-5.x) accept neither
temperature/top_p nor stop, seed and the penalties, so sending one to those models is a 400 that names the parameter and the model, rather than a 200 that silently ignored it; Claude refuses a temperature above 1 with a 400; and reasoning_effort is folded onto the nearest rung of a backend’s own scale (xhigh and max both reach Anthropic as max).Images
Animage_url part takes a data: URI or an http(s) URL, and only on a user message. One request may carry at most 10 images, 20 MB each and 40 MB in total. Inlined data: URIs also count towards the request size limit, so send larger images as URLs. The internal storage:// scheme is refused — reach knowledge base assets through knowledge instead.
Structured output
Which oftext, json_object and json_schema a model supports depends on the backend serving it, which the model id does not reveal — capabilities.response_formats on its model entry lists them:
- Served by OpenAI —
json_schemaandjson_object - Served by Anthropic —
json_schemaonly; there is no free-form JSON mode - Served by Bedrock or Google — neither; these have no way to receive a schema
400.
Finish reasons
finish_reason is stop for a complete answer, length when the output reached the token cap and is worth continuing, and content_filter when the provider withheld it. It is never tool_calls: tools run server-side, and the caller only ever receives the final answer.
Prompt caching
Prompt caching is on by default and needs nothing in the request. When a request begins with the same tokens as a recent request to the same model, the provider can reuse that prefix instead of processing it again, which mainly shortens the wait on long prompts and long conversations. How the prefix gets cached depends on the backend serving the model:
To get the most out of it, keep the start of your requests stable: the
system or developer message first, earlier turns unchanged and in order, and whatever changes from one request to the next — the new question, per-request data — at the end. Rewriting an earlier message changes the prefix from that point on.
usage.prompt_tokens_details.cached_tokens reports how many of the prompt_tokens were read from the cache, on a non-streamed response and on the final usage chunk of a stream sent with stream_options.include_usage. It is 0 when nothing was cached, including on backends that do not report cache reads.
OpenAI’s prompt_cache_key, prompt_cache_options and prompt_cache_retention are refused with a 400: caching is managed for you, and a key or retention setting could not be honoured on every backend.
nuwacom extensions
knowledge is the only field this endpoint adds to OpenAI’s body. The OpenAI SDKs have no argument for it, so it goes in extra_body (Python) or the provider’s extra-body option.
asset is an uploaded file in the space’s knowledge base, content is a document authored in nuwacom, folder is expanded into the assets and contents it holds, space (no id) is the whole space’s knowledge base, and web (no id) is web search.
Grounding the answer
Retrieval is opt-in:space cannot be combined with asset, content or folder references; web combines with any of them. Retrieval and web search run as agent tools, so a model without tool support refuses knowledge with a 400 — its unsupported_parameters lists knowledge.
knowledge is the whole retrieval scope, whatever the space’s skills say: a skill that needs the knowledge base, or one with documents attached to it, only searches what knowledge allows. To give such a skill its documents, send { "type": "space" } or pin them.
Web search
Web search is opt-in, like retrieval. Add{ "type": "web" } to knowledge and the agent may search the web when the question needs it; leave it out and no query leaves nuwacom. The agent decides whether a search is needed rather than searching on every request. This holds whatever the space’s skills say: a skill that needs web search only searches when the request includes web.
web_search_options is refused with a 400 that points here, including when a framework sends it for you (Pydantic AI’s WebSearchTool does).
Agents have their own API — see the Agents API.
Limits
A non-streamed request sends nothing until the whole run has finished, retrieval included. For long, grounded answers prefer
stream: true: tokens arrive as they are produced, and the connection is not left idle at the gateway.
Errors
Every failure is answered in OpenAI’s envelope, whatever the status, so an SDK raises the exception it would against OpenAI itself:
A streamed request has already sent its
200 and headers before the first model call, so a failure mid-stream arrives as a data: {"error": {...}} frame — the same envelope, same vocabulary — immediately before data: [DONE].