Skip to content

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:

  1. 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_id is required on thread creation and cannot be changed afterward.
  2. Every run completes synchronously. A run is created already completed or failed — there is no queued / in_progress state and no requires_action / submit_tool_outputs handshake. POST .../runs/:runId/cancel always returns 400, since there is never anything in flight to cancel by the time a caller could reach it.
  3. Client-executed function tools are not supported — that contract needs the run to pause and wait for a submitted result, which departure (2) rules out. code_interpreter has no backing runtime on this dialect yet. file_search maps to the agent's Knowledge Engine module. Console's own tool/MCP/system catalog is reachable through the console_tool extension below.

Assistants ​

Create ​

POST /api/client/v1/assistants
json
{
  "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"] }
  }
}
FieldTypeRequiredDescription
modelstringYesModel key from the Model Hub.
namestringNoDefaults to Assistant (<model>).
descriptionstringNo
instructionsstringNoMaps to the agent's system prompt.
temperaturenumberNo
top_pnumberNo
toolsarrayNoSee Tools below.
tool_resourcesobjectNofile_search.vector_store_ids — a one-element array naming a Knowledge Engine module key.
metadata.guardrailsobjectNoConsole 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/:assistantId

POST 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 ​

typeBehavior
file_searchRequires tool_resources.file_search.vector_store_ids naming exactly one Knowledge Engine module key. Maps to the agent's knowledgeEngineKey.
console_toolConsole extension. { "type": "console_tool", "tool": { "source": "tool"|"mcp"|"system", "sourceKey": "...", "toolNames": ["..."] } } — reaches the same tool/MCP/system catalog the dashboard's tool picker does.
functionRejected with 400. Needs the async pause/resume contract this dialect doesn't implement (see departure 3 above).
code_interpreterRejected 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
json
{
  "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/:threadId

POST 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
json
{ "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/:messageId

Listing 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/runs

Runs the thread's one pending message through executeAgentChat — the same tool loop, guardrails and tracing a /agents/responses call gets — and returns already-terminal:

json
{
  "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/runs

client.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/cancel

cancel always returns 400 — see departure 2 above.

Errors ​

StatusDescription
400Missing/invalid required field, unsupported tool type, a second pending message, or nothing to run
401Invalid API token
404Assistant, thread, message, or run not found
500Internal error

Where to go next ​

  • Agents — the underlying engine and its own /agents/responses dialect.
  • Build and publish an agent — configure the model, tools and guardrails an Assistant inherits.
  • Guardrails — attach policies via metadata.guardrails at create time.

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub