Skip to main content
IronClaw exposes an OpenAI-compatible Responses API. Unlike a raw LLM passthrough, requests route through the full agent loop, so callers get tools, memory, safety, and server-side conversation state via a standard wire format.
The legacy /v1/responses path is still accepted as an alias for clients pinned to it.

When to use this vs. the Chat Completions proxy

If you want IronClaw’s capabilities behind an OpenAI-shaped wire contract, this is the endpoint.

Authentication

Every protected route requires a bearer token in the Authorization header:
You have two options:
The bearer token provisioned during onboarding authenticates both the web interface and this API. It is written to webui-token in your IronClaw home:
Supply your own instead by setting IRONCLAW_REBORN_WEBUI_TOKEN before starting serve.
This token carries operator privileges, including configuration changes. For anything beyond a single-user instance, use per-user tokens instead.
A missing or wrong token returns 401. Chat send endpoints (including this one) are rate-limited to 30 requests per 60 seconds per user.

Quickstart

The simplest request, with no session state and no tools:
Response:
The OpenAI Python and TypeScript SDKs work as-is. Point base_url at https://your-host/api/v1 (or /v1 for the alias).

Request fields

These fields are rejected with 400 so callers know they were ignored rather than silently dropped:
  • tool_choice (no per-request tool surface to enforce against)
  • temperature (configure via settings)
  • max_output_tokens (not yet wired)
  • Any model other than "default"

Session continuity

Each response embeds its thread UUID in the id. Pass the previous id back as previous_response_id to continue the same conversation:
IronClaw replays the thread’s history from the conversation store and runs the new turn against it. There is no client-side state to manage. Threads are user-scoped, so a token belonging to a different user cannot resume someone else’s thread (the lookup returns 404). Each POST mints a fresh response_uuid, so two turns on the same thread produce different ids.

Retrieving a past response

Returns the output items reconstructed from the stored conversation. Token usage is not retained per-message; the usage field on a retrieved response is zero.

Streaming

Set "stream": true to receive Server-Sent Events:
Events follow the OpenAI Responses streaming format. The SSE event: field matches the JSON type: A keepalive frame fires every 15 seconds to prevent intermediate proxies from closing idle connections. The non-streaming path has a 120-second turn timeout. Long-running tool work (sandbox jobs, multi-step agentic flows) should use stream: true so the connection stays responsive.

External tools

You can register your own function tools per request. The agent treats them as first-class actions alongside built-in tools and pauses execution when it wants to call one. Your client executes the call and feeds the result back on the next request. This is a function-calling round-trip, not a prompt-level convention. The wire shape matches the OpenAI Responses API spec.

Define tools

Constraints (validated up-front, 400 on violation):
  • Only type: "function" is accepted. web_search, file_search, code_interpreter, etc. are rejected. IronClaw routes those through its own registry, not caller-provided definitions.
  • Names: ^[A-Za-z0-9_-]{1,64}$, unique within the request.
  • Names must not shadow a registered IronClaw action (built-in tool, extension tool, or an agent capability like mission_*, skill_*, memory_*). Shadowing is rejected to prevent confused-deputy behavior.
  • The entire tools[] payload caps at 16 KiB of canonical JSON.

The round trip

When the model calls one of your tools, the response completes with a function_call output item and the thread sits in Waiting:
The model may emit prose before the call. Any pre-call text is flushed as a leading message item so you see both pieces in order. Execute the call locally, then POST a follow-up with a function_call_output item. Include previous_response_id so the resume targets the same thread:
The agent resumes, the LLM sees your tool result, and the final answer comes back as a normal message output item. Pass the same tools[] on the resume request. The catalog is per-thread; passing the definitions keeps the tool available for any follow-up calls the model makes.

Resume validation

The resume request must satisfy the bridge:
  • A function_call_output item without a live external-tool gate on this thread returns 400.
  • A function_call_output item whose call_id does not match the pending gate returns 400.
  • A function_call_output item with an empty call_id or missing output returns 400.
  • If the thread is paused on an unrelated gate (OAuth, approval, pairing), resolve that first.
You can interleave a fresh user message with the tool output by adding a message item. Both will be visible to the agent:

Multi-call batching

The engine pauses on the first external tool call in an assistant turn. If the model wants to invoke tool_a and tool_b together, only tool_a surfaces on the first response. After you resume, tool_b is emitted on the next turn. This is a known limitation; OpenAI-style “post all results together” is a follow-up.

Streaming flow

With stream: true, the external-tool flow looks like:
  1. response.created
  2. Optional response.output_text.delta events for any leading prose.
  3. response.output_item.added with the function_call item.
  4. response.output_item.done with the same item.
  5. response.completed.
The stream closes after response.completed. Send the resume as a fresh POST with previous_response_id.

Structured context (x_context)

For integrations that need to pass structured state alongside the user message (notification approval, webhook payload, environment hints), use x_context:
The handler renders it as a <user-context> block ahead of the user message. Total serialized size caps at 10 KB. Pass a flat {key: {object}} structure; deeper nesting is serialized as raw JSON. x_context is an IronClaw extension and is not part of the OpenAI Responses API spec.

Per-request instructions

Use instructions to inject a one-turn system/developer message:
IronClaw caveat — diverges from the OpenAI spec. The OpenAI Responses API contract is that instructions apply only to the current turn and are not carried by previous_response_id. IronClaw currently prepends the <instructions> block into the user message and persists it as part of the conversation, so it is visible on later turns when history is replayed. Until the handler stores instructions out-of-band, treat them as sticky for the thread and re-send (or override) them explicitly on each turn that needs different behaviour. The agent’s persistent identity files (AGENTS.md, SOUL.md, USER.md, IDENTITY.md) come from workspace memory and are not affected by this field.

Errors

Errors from the Responses handler use the OpenAI envelope:
Common codes: A turn that completes but fails mid-flight (the model errored, a required tool raised) returns 200 with "status": "failed" and a populated error field. Inspect status before reading output. Two cases do not use the JSON envelope and need separate handling:
  • 401 Unauthorized comes from the gateway auth middleware before the request reaches the handler, and the body is a plain-text string (Invalid or missing auth token). Same for 403 (Forbidden for OIDC domain violations) and the 503 (Database unavailable) emitted by the middleware when the token store is down.
  • GET /api/v1/responses/{id} returns 404 (still JSON-enveloped) when the response id is unknown or the thread does not belong to the authenticated user. POST with a foreign previous_response_id does not return 404; the handler only decodes the UUID and dispatches into the agent, where the cross-user resume surfaces as a turn-level failure rather than an HTTP error. Treat cross-user resume as undefined and avoid relying on the response shape.
The Responses API does not support interactive approvals or authentication gates in the response stream. If the agent hits a tool that requires user approval (e.g. shell with a destructive command) or an extension OAuth flow, the turn fails with a clear error directing you to resolve the gate via the web UI or a different channel.

SDK usage

The OpenAI SDKs are the easiest way to use this endpoint. Point them at IronClaw and they “just work”:

Limits and quirks

  • Rate limit: 30 requests per 60 seconds per user (shared with /api/chat/send).
  • Body size: 14 MiB request limit at the gateway.
  • Turn timeout (non-streaming): 120 seconds. Use stream: true for long-running work.
  • Tool batching: one external-tool call per round trip. The engine resumes the next call on the next turn.
  • Approvals and auth gates: not surfaced over the Responses API. Resolve them via the web UI before retrying.
  • tool_choice, temperature, max_output_tokens: not yet supported. Requests carrying these fields are rejected so callers see they were not honoured.
  • model: must be "default". Provider and model selection is server-side via settings.
  • Token usage on retrieval: GET /api/v1/responses/{id} returns reconstructed output items but usage is zero because per-message token counts are not persisted.

  • Inference Providers — configure the model that backs "default".
  • Configuration — configuration file layout, profiles, and environment overrides.
  • Sandboxed Tools — how IronClaw’s built-in tools execute when the agent calls them.
  • MCP — connect Model Context Protocol servers to extend the agent’s tool surface server-side instead of supplying caller tools per-request.