OpenResponses API: Expose /v1/responses Endpoint from Gateway

Learn how to enable an OpenResponses-compatible HTTP endpoint on your Gateway. This guide covers configuration, authentication, and routing for developers using the Gateway.

Read this when

  • Integrating clients that speak the OpenResponses API
  • You want item-based inputs, client tool calls, or SSE events

The Gateway can expose an POST /v1/responses endpoint that is compatible with OpenResponses. This feature is turned off by default and operates on the same port as the Gateway itself (via WS + HTTP multiplexing): http://<gateway-host>:<port>/v1/responses.

Requests follow the same execution path as a standard Gateway agent run (identical code path to openclaw agent), meaning routing, permissions, and configuration align with your existing Gateway setup.

Toggle this feature on or off using gateway.http.endpoints.responses.enabled. When activated, the same compatibility layer also provides GET /v1/models, GET /v1/models/{id}, POST /v1/embeddings, and POST /v1/chat/completions.

Authentication, security, and routing

Runtime behavior follows the OpenAI Chat Completions specification:

  • The authentication path mirrors gateway.auth.mode: shared-secret mode (token/password) relies on Authorization: Bearer <token-or-password>; trusted-proxy mode uses identity-aware proxy headers (same-host loopback proxies require gateway.auth.trustedProxy.allowLoopback = true, with a direct same-host fallback through gateway.auth.password / OPENCLAW_GATEWAY_PASSWORD when no Forwarded/X-Forwarded-*/X-Real-IP header exists); none on private ingress does not need an auth header. Refer to Trusted proxy auth.
  • Consider the endpoint as granting full operator access to the gateway instance.
  • Shared-secret auth modes disregard a more restrictive bearer-declared x-openclaw-scopes and restore the complete default operator scope set: operator.admin, operator.approvals, operator.pairing, operator.read, operator.talk.secrets, operator.write. Chat turns on this endpoint are handled as owner-sender turns.
  • Trusted identity-bearing HTTP modes (trusted-proxy, or gateway.auth.mode="none") respect x-openclaw-scopes when provided, otherwise they fall back to the operator default scope set. Owner semantics are only lost if the caller explicitly narrows scopes and leaves out operator.admin.
  • Choose agents using model: "openclaw", "openclaw/default", "openclaw/<agentId>", or the x-openclaw-agent-id header.
  • Apply x-openclaw-model to replace the selected agent's backend model (requires operator.admin on identity-bearing auth paths).
  • Use x-openclaw-session-key for explicit session routing (returns 400 invalid_request_error if it involves a reserved namespace: subagent:, cron:, acp:).
  • Use x-openclaw-message-channel to specify a non-default synthetic ingress channel context.

For a complete description of agent-target models, openclaw/default, embeddings pass-through, and backend model overrides, consult OpenAI Chat Completions.

Check Operator scopes and Security for more details.

Session behavior

The endpoint is stateless per request by default, meaning a fresh session key is generated for every call.

When a request contains an OpenResponses user string, the Gateway creates a stable session key from it, allowing repeated calls to share an agent session.

previous_response_id reuses the session from the prior response as long as the request stays within the same agent, user, and requested session scope (matched by auth subject, agent id, and x-openclaw-session-key).

Request shape

FieldSupport
inputCan be a string or an array of item objects.
instructionsGets folded into the system prompt.
toolsDefinitions for client tools (function tools).
tool_choiceUse "auto", "none", "required", or { "type": "function", "name": "..." } to restrict or mandate which client tools are used.
streamTurns on SSE streaming.
max_output_tokensA best-effort cap on output length (depends on the provider).
temperatureBest-effort sampling temperature. The ChatGPT-based Codex Responses backend ignores it, applying a fixed server-side value instead.
top_pBest-effort nucleus sampling. The same Codex Responses limitation applies as with temperature.
userEnsures stable session routing.
previous_response_idMaintains session continuity (described above).
max_tool_calls, reasoning, metadata, store, truncationAccepted but produce no effect at this time.

Items (input)

message

Roles: system, developer, user, assistant.

  • system and developer get added to the end of the system prompt.
  • The most recent user or function_call_output entry serves as the "current message."
  • Earlier user and assistant messages are supplied as conversation history.

function_call_output (turn-based tools)

Return tool results to the model:

{
  "type": "function_call_output",
  "call_id": "call_123",
  "output": "{\"temperature\": \"72F\"}"
}

reasoning and item_reference

Accepted for schema compatibility but omitted when constructing the prompt.

Tools (client-side function tools)

Provide tools using tools: [{ type: "function", name, description?, parameters? }].

When the agent invokes a tool, the response includes an function_call output item. To continue the turn, send a follow-up request containing function_call_output.

For tool_choice: "required" and function-pinned tool_choice, the endpoint reduces the set of exposed client function tools, directs the runtime to call a client tool before replying, and rejects the turn if it lacks a matching structured client-tool call, consistent with the /v1/chat/completions contract. Non-streaming requests return 502 with an api_error; streaming requests produce a response.failed event.

Images (input_image)

Accepts base64 or URL sources:

{
  "type": "input_image",
  "source": { "type": "url", "url": "https://example.com/image.png" }
}

Permitted MIME types (default): image/jpeg, image/png, image/gif, image/webp, image/heic, image/heif. Default maximum size: 10MB.

Files (input_file)

Accepts base64 or URL sources:

{
  "type": "input_file",
  "source": {
    "type": "base64",
    "media_type": "text/plain",
    "data": "SGVsbG8gV29ybGQh",
    "filename": "hello.txt"
  }
}

Permitted MIME types (default): text/plain, text/markdown, text/html, text/csv, application/json, application/pdf. Default maximum size: 5MB.

Current behavior:

  • File contents are decoded and inserted into the system prompt rather than the user message, making them temporary (they are not stored in session history).
  • Before addition, decoded file text is enclosed as untrusted external content, so file bytes are handled as data rather than trusted commands. The injected block uses explicit boundary markers (<<<EXTERNAL_UNTRUSTED_CONTENT id="...">>> / <<<END_EXTERNAL_UNTRUSTED_CONTENT id="...">>>) together with a Source: External metadata line. The lengthy SECURITY NOTICE: banner is deliberately left out to conserve the prompt budget; the boundary markers and metadata remain active.
  • PDFs are initially scanned for text. When minimal text is detected, the initial pages are converted to raster images and sent to the model, with the injected file block employing the placeholder [PDF content rendered to images].

PDF processing relies on the bundled document-extract plugin, which leverages clawpdf along with its included PDFium WebAssembly runtime for both text extraction and page rendering.

Default settings for URL fetching:

  • files.allowUrl: true
  • images.allowUrl: true
  • maxUrlParts: 8 (combined total of input_file plus input_image parts per request)
  • Requests are protected by DNS resolution checks, private IP blocking, redirect limits, and timeouts.
  • Per input type (files.urlAllowlist, images.urlAllowlist), optional hostname allowlists are supported: exact host matching ("cdn.example.com") or wildcard subdomains ("*.assets.example.com", which does not match the apex domain). An empty or omitted allowlist means no hostname allowlist restriction is applied.
  • To completely disable URL-based fetches, set files.allowUrl: false and/or images.allowUrl: false.

File + image limits

The endpoint enforces a built-in 20 MB request-body limit. File and image source policy remains adjustable under gateway.http.endpoints.responses:

{
  gateway: {
    http: {
      endpoints: {
        responses: {
          enabled: true,
          maxUrlParts: 8,
          files: {
            allowUrl: true,
            urlAllowlist: ["cdn.example.com", "*.assets.example.com"],
            allowedMimes: [
              "text/plain",
              "text/markdown",
              "text/html",
              "text/csv",
              "application/json",
              "application/pdf",
            ],
            maxBytes: 5242880,
            maxChars: 60000,
            maxRedirects: 3,
            timeoutMs: 10000,
            pdf: {
              maxPages: 4,
              maxPixels: 4000000,
              minTextChars: 200,
            },
          },
          images: {
            allowUrl: true,
            urlAllowlist: ["images.example.com"],
            allowedMimes: [
              "image/jpeg",
              "image/png",
              "image/gif",
              "image/webp",
              "image/heic",
              "image/heif",
            ],
            maxBytes: 10485760,
            maxRedirects: 3,
            timeoutMs: 10000,
          },
        },
      },
    },
  },
}

Values used when not explicitly provided:

KeyDefault
maxUrlParts8
files.maxBytes5MB
files.maxChars60k
files.maxRedirects3
files.timeoutMs10s
files.pdf.maxPages4
files.pdf.maxPixels4,000,000
files.pdf.minTextChars200
images.maxBytes10MB
images.maxRedirects3
images.timeoutMs10s

HEIC/HEIF input_image sources are converted to JPEG before provider delivery using the shared OpenClaw image processor (Rastermill), which falls back to a system converter (sips, ImageMagick, GraphicsMagick, or ffmpeg) for formats that need external codec support.

Security note: URL allowlists are applied both before the fetch and on redirect hops. Allowing a hostname does not bypass private or internal IP blocking. For internet-facing gateways, apply network egress controls alongside application-level safeguards. See Security.

Streaming (SSE)

Set stream: true to receive Server-Sent Events:

  • Content-Type: text/event-stream
  • Each event line is event: <type> and data: <json>
  • The stream terminates with data: [DONE]

Currently emitted event types: response.created, response.in_progress, response.output_item.added, response.content_part.added, response.output_text.delta, response.output_text.done, response.content_part.done, response.output_item.done, response.completed, response.failed (on error).

Usage

usage is filled when the underlying provider reports token counts. OpenClaw normalizes common OpenAI-style aliases before those counters reach downstream status and session surfaces, including input_tokens / output_tokens and prompt_tokens / completion_tokens.

Errors

Errors are returned as a JSON structure:

{ "error": { "message": "...", "type": "invalid_request_error" } }

Typical error scenarios include: 400 malformed request body, 401 authentication that is missing or invalid, 403 operator scope not granted, 405 HTTP method not allowed, 429 repeated authentication failures exceeding the limit (accompanied by Retry-After).

Examples

Without streaming:

curl -sS http://127.0.0.1:18789/v1/responses \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -H 'x-openclaw-agent-id: main' \
  -d '{
    "model": "openclaw",
    "input": "hi"
  }'

With streaming:

curl -N http://127.0.0.1:18789/v1/responses \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -H 'x-openclaw-agent-id: main' \
  -d '{
    "model": "openclaw",
    "stream": true,
    "input": "hi"
  }'
1,511 words · updated Jul 27, 2026