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 are rejected with 400 so callers know they were not honoured:
  • temperature outside 0.02.0
  • A malformed model (empty, surrounding whitespace, over 256 bytes, or control characters)
  • x_context larger than 10 KB
Other OpenAI request fields not listed above (for example max_output_tokens) are accepted and ignored: the endpoint deliberately tolerates unknown fields so newer SDK payloads keep working, but unlisted fields have no effect.
External-tools support is always wired on a standard ironclaw serve deployment, which is why tools and tool_choice are accepted above. A custom composition that mounts the Responses routes without external-tool wiring instead rejects non-empty tools and any tool_choice with 400 naming the field.

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. The usage field is read best-effort from persisted run state (token totals plus USD cost); it can be zero when the run reported no usage.

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.
  • A resume request that omits previous_response_id returns 400.
  • A resume input array must contain only function_call_output items — mixing in a message item returns 400.
  • If the thread is paused on an unrelated gate (OAuth, approval, pairing), resolve that first.
To ask a follow-up question alongside a tool result, complete the resume first, then send the new message as its own request with previous_response_id pointing at the resumed response.

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): 30 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: accepted and ignored — there is no per-request tool-choice surface. max_output_tokens and other unlisted OpenAI fields are likewise accepted and ignored.
  • model: required, any well-formed name (≤ 256 bytes). It is a routing hint echoed back in the create response; GET and cancel responses report the server placeholder "reborn". Provider and model selection is server-side via settings.
  • Token usage on retrieval: GET /api/v1/responses/{id} reads usage best-effort from persisted run state (token totals plus USD cost); it can be zero when the run reported no usage.

  • 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.