OpenAI Responses integration
Workflow Code can expose an adapted Conversation project as an OpenAI Responses API service. Desktop starts a local gateway on 127.0.0.1:7135 by default. Local projects execute inside Desktop, while server projects are transparently streamed to the hosted project API on the currently connected Workflow Server. A client can also call the Server /v1 endpoint directly.
This endpoint implements a Responses, Conversations, and Models subset. It does not provide /v1/chat/completions or the legacy /v1/completions endpoint.
Get connection details in Desktop
Every Conversation project exposes OpenAI API in its tool catalog:
- Open the Conversation project details and select OpenAI API from New Tab.
- Check the project adapter, Desktop gateway, and Server service states.
- Select a model available to the current account.
- Create a Key for the current user and project, or rotate the active Key.
- Copy the base URL, Responses URL, and curl, Node.js, or Python example.
The Desktop gateway listens on loopback only and cannot accept LAN connections. Its default addresses are:
Base URL: http://127.0.0.1:7135/v1
Responses URL: http://127.0.0.1:7135/v1/responses
A server project also displays the direct Server base URL. Calls to a server project through the Desktop address are streamed unchanged to the current Server; the Server still validates the Key.
If port 7135 is already in use, only the OpenAI gateway is disabled. The rest of Desktop keeps running, and the OpenAI API page reports the port error. Release the port and restart Desktop to recover the gateway.
A Conversation project without an adapter still displays the OpenAI API page and documentation link, but it cannot create, rotate, or use a project Key.
Key and identity boundary
A project Key uses the wfpk_* format and binds all of the following:
- the currently signed-in user;
- one Conversation project;
- the project's currently available OpenAI adapter.
Public calls therefore use the standard /v1/responses path without a custom project Header or parameter. /v1 accepts project Keys only. Desktop sign-in credentials, account API Keys, and the Server administrator Key cannot replace one.
The full Key is shown once, immediately after creation or rotation. Desktop SQLite and Server PostgreSQL store only a SHA-256 hash and recognizable prefix, so the full value cannot be retrieved later. Rotate a lost Key. Rotation and revocation invalidate the old Key immediately without deleting existing Response or Conversation resources.
Do not put a project Key in source code, project environment files, browser bundles, screenshots, or logs. Prefer process environment variables:
export OPENAI_API_KEY='wfpk_...'
export OPENAI_BASE_URL='http://127.0.0.1:7135/v1'
Examples
curl
curl "$OPENAI_BASE_URL/responses" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"model": "provider/model-id",
"input": "Summarize this content."
}'
Node.js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
baseURL: process.env.OPENAI_BASE_URL,
});
const response = await client.responses.create({
model: "provider/model-id",
input: "Summarize this content.",
});
console.log(response.output_text);
Python
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["OPENAI_API_KEY"],
base_url=os.environ["OPENAI_BASE_URL"],
)
response = client.responses.create(
model="provider/model-id",
input="Summarize this content.",
)
print(response.output_text)
Call GET /v1/models first to obtain the model IDs actually available to the current Key. The Server follows the current user's Provider preference and selects the first valid credential that exposes that model. Hard capabilities such as image input still return an explicit error when unavailable. Accepted optional sampling fields are passed through to the Provider; when an AI SDK Provider reports an unsupported or compatibility warning, the request continues and the downgrade details remain in response Provider metadata.
Supported endpoints
| Resource | Endpoints |
|---|---|
| Responses | POST /v1/responses |
| Response resources | GET /v1/responses/{id}, DELETE /v1/responses/{id} |
| Cancellation | POST /v1/responses/{id}/cancel |
| Response input | GET /v1/responses/{id}/input_items |
| Conversations | POST /v1/conversations, GET/POST/DELETE /v1/conversations/{id} |
| Conversation items | GET/POST /v1/conversations/{id}/items, GET/DELETE /v1/conversations/{id}/items/{item_id} |
| Models | GET /v1/models, GET /v1/models/{model} |
List endpoints accept after, limit, and order=asc|desc, and return object: "list", data, first_id, last_id, and has_more.
POST /v1/responses supports these request fields:
| Field | Supported behavior |
|---|---|
model | Required and must come from /v1/models for the current Key. |
input | A string or an array of user, assistant, system, and developer messages. |
instructions | Optional top-level instructions for this turn. |
temperature, top_p, max_output_tokens | Passed to the project and Provider; actual support depends on model capabilities. |
text | Plain-text format.type: "text" only; verbosity accepts low, medium, or high. |
include | Accepts only the compatibility value reasoning.encrypted_content; this subset emits no reasoning item, so it adds no output. |
store | Defaults to true. Controls Response retention but does not delete explicit Conversation items. |
stream | Returns standard Responses SSE. Mutually exclusive with background. |
background | Returns queued immediately and supports later polling or cancellation. |
previous_response_id | Continues a retained Response whose status is completed or incomplete. Mutually exclusive with conversation. |
conversation | A persistent Conversation ID or { "id": "conv_*" }; the object form must contain a valid id. |
metadata | Up to 16 string key-value pairs stored with the API resource. |
Unknown fields, conflicting fields, and parameters outside the protocol subset return invalid_request_error; they are not ignored. An accepted optional field that the underlying Provider marks as unsupported is handled as a Provider warning and does not block execution. Every error uses this shape:
{
"error": {
"message": "...",
"type": "invalid_request_error",
"param": "input",
"code": "invalid_value"
}
}
A Response object sets completed_at only when status is "completed". It is null for queued, in_progress, failed, cancelled, and incomplete responses.
When the Provider finish reason indicates an output limit or content filtering, the gateway returns status: "incomplete" and sets incomplete_details.reason to "max_output_tokens" or "content_filter", respectively. Text already generated remains in output, and the corresponding message item also has status incomplete.
Input and images
Message content can be a string or use these content parts:
- user:
input_textandinput_image; - assistant history:
output_text, withinput_textalso accepted and normalized; system/developer:input_text, handled as higher-priority model instructions by the built-in adapters;- image: an HTTP/HTTPS URL or image data URL.
The built-in Conversation, Codex, OpenCode, and Project Assistant adapters explicitly apply text.verbosity; they do not merely accept and ignore it. Project Assistant reuses OpenCode's adapter and stable output item. include: ["reasoning.encrypted_content"] is a legacy client-compatibility value that OpenAI still accepts. It does not mean this gateway implements reasoning items.
The effective input must contain user text or an image. Images are limited to JPEG, PNG, WebP, and GIF. SVG and general files are not supported in the first version.
Images enter a restricted downloader and the existing managed file store. The limits are 10 MiB per image, 20 MiB combined per request, at most 16 images, a 10-second download timeout, and at most 3 redirects. The gateway verifies the file signature against its MIME type and rejects compressed HTTP responses, embedded URL credentials, loopback, private, link-local, reserved, cloud metadata, and any redirected non-public address.
Multi-turn state
Two mutually exclusive state mechanisms are available:
previous_response_idadds a priorcompletedorincompleteResponse's input and output to the current context;conversationreads items from a persistent Conversation and atomically appends the current input and output after success.
previous_response_id does not carry over top-level instructions from the previous response. Resend stable instructions on every turn:
const first = await client.responses.create({
model,
input: "First question",
instructions: "Always answer in concise English.",
});
const second = await client.responses.create({
model,
input: "Continue the explanation",
previous_response_id: first.id,
instructions: "Always answer in concise English.",
});
An explicit Conversation appends the current input and available output when the run completes or ends as incomplete; failure and cancellation append nothing. Deleting a Conversation removes only the Conversation resource, while existing items and their image references remain as required by the Responses protocol. Deleting one item returns the updated Conversation object and reclaims its image only when no other resource references that image.
Streaming and background work
stream: true uses standard Responses SSE events in this order:
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.incomplete
A normally completed stream ends with response.completed. A stream stopped by an output limit or content filter retains partial text and ends with response.incomplete. An in-stream failure sends error and response.failed. The stream does not use the Chat Completions [DONE] sentinel. Disconnecting a foreground client cancels the underlying Desktop or Server run.
background: true and stream: true are mutually exclusive in the first version. Background creation returns queued immediately. Poll with GET /v1/responses/{id} and cancel idempotently with POST /v1/responses/{id}/cancel. When Desktop or Server restarts, leftover queued and in_progress responses become failed; the public error is server_error, the internal audit category is server_restart, and the first version does not recover work across processes.
Reverse proxy SSE
OpenAI project access details prefer WORKFLOW_API_PUBLIC_BASE_URL. It must be an HTTP(S) URL without embedded credentials and is normalized to its origin; production reverse proxies should set it explicitly. If it is absent or invalid, Server uses the current request protocol and Host. X-Forwarded-Proto and X-Forwarded-Host are honored only when Express trusts the directly connected proxy, so an ordinary client cannot change the displayed address with those headers.
A reverse proxy must disable SSE buffering and caching. For example, with Nginx:
location /v1/ {
proxy_pass http://127.0.0.1:7130;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
add_header X-Accel-Buffering no;
}
Do not let a CDN, Ingress, or application proxy aggregate text/event-stream chunks.
Retention, deletion, and privacy
| Resource | Default retention |
|---|---|
A Response with store: true or omitted | 30 days. |
A foreground store: false Response | Removed when the request finishes and cannot be retrieved. |
A background store: false Response | Retained for 10 minutes after reaching a terminal state for polling. |
| Conversation | Retained until explicitly deleted. |
| Conversation items | Retained until deleted individually; deleting the containing Conversation does not delete its items. |
Explicit Conversation items are independent of Response store: false. Desktop cleans SQLite at startup and periodically; Server does the same for PostgreSQL. Deleting or expiring a Response, or deleting an individual Conversation item, reclaims managed images no longer referenced by another resource. Deleting the Conversation itself does not reclaim images used by its retained items.
The API resource store retains request and output bodies according to this policy. Every OpenAI call immediately creates an OpenAI Responses entry in the target project's run history when execution starts, then updates that entry in place on completion, failure, or cancellation. A Desktop or Server restart also finalizes an already-started entry as failed. Its details retain and display the complete normalized request, standard Response, Workflow execution report, node inputs and outputs, stdout, stderr, errors, finish reason, and Provider metadata. store: false controls only Responses protocol resource retention and does not trim the project run record.
The full wfpk_* Key and Provider credentials are not written to run records; only the Key ID and prefix are retained. Audit records continue to store identity, project, model, status, timestamps, usage, error category, and internal run ID without copying business bodies. Ordinary Conversation history also does not gain a duplicate message set automatically from an API call.
Project adapter
The framework owns HTTP protocol behavior, Keys, authentication and authorization, IDs, validation, state machines, SSE, background work, persistence, image security, cleanup, and auditing. A Conversation project developer only maps typed host input into project input and returns text through the declared output item.
Declare the adapter in conversation.openai on the executor:
const ANSWER_ITEM_ID = "assistant-response";
export const executor = workflow.defineExecutor({
projectType: "conversation",
workflow: conversationWorkflow,
conversation: {
openai: {
enabled: true,
outputItemId: ANSWER_ITEM_ID,
},
},
});
At runtime, the project reads workflow.readOpenAIConversationHostInput(context) to obtain:
- all normalized
messages; - current-turn
instructions; model;temperature,topP,maxOutputTokens, andverbosity;- the API Conversation ID and project-run body-persistence setting.
The project must keep outputItemId stable, write streaming deltas and final text to that item. A project may explicitly validate hard capabilities or parameters it cannot represent; with the Core AI SDK Provider, an unsupported or compatibility warning for an optional field does not automatically stop execution. The framework writes the complete exchange and execution report to project run history; the project does not need to copy the same bodies into ordinary Conversations, KV, or audit records unless its own business semantics require that duplication.
Not supported yet
The following features are framework TODOs. The two known endpoints explicitly return an OpenAI-shaped 404 invalid_request_error; the remaining capabilities are unimplemented and are never silently ignored:
/v1/responses/compactand/v1/responses/input_tokens;- Structured Outputs, reasoning parameters and reasoning items, general files, Web Search, File Search, MCP, and audio;
- WebSocket, logprobs, prompt cache, and service tier;
- standard function/tool calling items, call IDs, stream events, and state recovery.
/compact is not a command that each project developer must implement. The framework must first provide the protocol and recovery behavior for tool calls; project developers then own tool definitions, permissions, and business execution.
/v1/chat/completions and /v1/completions are explicitly unsupported legacy protocols, not compatibility TODOs.