Assistants API
OpenAI's legacy Assistants surface (Assistants / Threads / Messages / Runs), wired onto the same Agent engine /client/v1/chat/completions and /client/v1/agents/responses already run. This is a wire adapter, not a second engine: an Assistant is an agent, a Thread is an agent conversation, and a Run is one agent-chat call — every enforcement path (guardrails, quota, tracing) runs exactly once, in the same place, regardless of which dialect a caller used.
Three departures from OpenAI's semantics
This dialect sits on an engine that runs synchronously and stores a conversation bound to one agent for its lifetime. Three behaviors follow from that, and are not bugs:
- A thread is bound to one assistant for its lifetime, set at creation. OpenAI's threads are assistant-agnostic; a run there supplies the assistant. Here,
assistant_idis required on thread creation and cannot be changed afterward. - Every run completes synchronously. A run is created already
completedorfailed— there is noqueued/in_progressstate and norequires_action/submit_tool_outputshandshake.POST .../runs/:runId/cancelalways returns400, since there is never anything in flight to cancel by the time a caller could reach it. - Client-executed
functiontools are not supported — that contract needs the run to pause and wait for a submitted result, which departure (2) rules out.code_interpreterhas no backing runtime on this dialect yet.file_searchmaps to the agent's Knowledge Engine module. Console's own tool/MCP/system catalog is reachable through theconsole_toolextension below.
Assistants
Create
POST /api/client/v1/assistants{
"model": "gpt-4o-mini",
"name": "Support Bot",
"instructions": "You are a helpful support agent.",
"tools": [
{ "type": "file_search" }
],
"tool_resources": {
"file_search": { "vector_store_ids": ["support-kb"] }
}
}| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Model key from the Model Hub. |
name | string | No | Defaults to Assistant (<model>). |
description | string | No | |
instructions | string | No | Maps to the agent's system prompt. |
temperature | number | No | |
top_p | number | No | |
tools | array | No | See Tools below. |
tool_resources | object | No | file_search.vector_store_ids — a one-element array naming a Knowledge Engine module key. |
metadata.guardrails | object | No | Console extension: attaches guardrail bindings the same way the dashboard's agent editor does. |
Returns an assistant object. id is asst_<agentKey>.
List / Retrieve / Modify / Delete
GET /api/client/v1/assistants?limit=20&order=desc
GET /api/client/v1/assistants/:assistantId
POST /api/client/v1/assistants/:assistantId
DELETE /api/client/v1/assistants/:assistantIdPOST accepts the same body fields as create — only the fields present are updated. tools is only re-evaluated when the field is present in the body, so partial updates never accidentally strip existing tool bindings.
Tools
type | Behavior |
|---|---|
file_search | Requires tool_resources.file_search.vector_store_ids naming exactly one Knowledge Engine module key. Maps to the agent's knowledgeEngineKey. |
console_tool | Console extension. { "type": "console_tool", "tool": { "source": "tool"|"mcp"|"system", "sourceKey": "...", "toolNames": ["..."] } } — reaches the same tool/MCP/system catalog the dashboard's tool picker does. |
function | Rejected with 400. Needs the async pause/resume contract this dialect doesn't implement (see departure 3 above). |
code_interpreter | Rejected with 400. Not backed by a runtime yet. |
An unrecognized or unsupported tool type is always refused with a 400 explaining why — never silently dropped, which would leave a caller believing a tool is armed when it is not.
Threads
Create
POST /api/client/v1/threads{
"assistant_id": "asst_support-bot",
"messages": [
{ "role": "user", "content": "My order hasn't arrived." }
]
}assistant_id is required (see departure 1). An optional seed messages array is accepted; if the last message is from the user, it becomes the thread's one pending message, ready for a run.
Retrieve / Modify / Delete
GET /api/client/v1/threads/:threadId
POST /api/client/v1/threads/:threadId
DELETE /api/client/v1/threads/:threadIdPOST merges metadata — the Assistants bookkeeping namespace (pending messages, run history) is server-owned and always carried forward, so a caller patching metadata can never erase it.
Messages
Create
POST /api/client/v1/threads/:threadId/messages{ "role": "user", "content": "Actually, it arrived — never mind." }Only one pending (un-run) message is allowed per thread at a time. Adding a user message while one is already pending returns 400; run the pending one first.
List / Retrieve
GET /api/client/v1/threads/:threadId/messages?limit=20&order=desc
GET /api/client/v1/threads/:threadId/messages/:messageIdListing returns persisted (already-run) messages followed by the still-pending one, in creation order. A message's id is stable across the pending → persisted transition — an id handed out before a run stays valid after it.
Runs
Create
POST /api/client/v1/threads/:threadId/runsRuns the thread's one pending message through executeAgentChat — the same tool loop, guardrails and tracing a /agents/responses call gets — and returns already-terminal:
{
"id": "run_...",
"object": "thread.run",
"thread_id": "thread_...",
"assistant_id": "asst_support-bot",
"status": "completed",
"created_at": 1725480000,
"completed_at": 1725480003,
"usage": { "prompt_tokens": 120, "completion_tokens": 48, "total_tokens": 168 },
"required_action": null
}If nothing is pending, returns 400. If the run fails (including a guardrail block), status is failed and last_error carries { code, message } — and, matching OpenAI's own semantics, the pending message is left in place so the caller can fix the problem and run again without re-sending it.
Create-and-run
POST /api/client/v1/threads/runsclient.beta.threads.createAndRun's convenience shape: creates a thread and immediately runs its final seed message in one call. Body is { assistant_id, thread: { messages: [...] } }.
List / Retrieve / Cancel
GET /api/client/v1/threads/:threadId/runs?limit=20&order=desc
GET /api/client/v1/threads/:threadId/runs/:runId
POST /api/client/v1/threads/:threadId/runs/:runId/cancelcancel always returns 400 — see departure 2 above.
Errors
| Status | Description |
|---|---|
| 400 | Missing/invalid required field, unsupported tool type, a second pending message, or nothing to run |
| 401 | Invalid API token |
| 404 | Assistant, thread, message, or run not found |
| 500 | Internal error |
Where to go next
- Agents — the underlying engine and its own
/agents/responsesdialect. - Build and publish an agent — configure the model, tools and guardrails an Assistant inherits.
- Guardrails — attach policies via
metadata.guardrailsat create time.

