API REFERENCE
Stream Chat
On this page
/api/chatStreams an agent loop running as a durable Vercel Workflow against the session's sandbox. The agent uses sandbox-only tools (bash, read, write, grep, glob, todo, task, ask_user_question, skill, fetch) — no MCP or Composio. Requires a sandbox provisioned via POST /api/sandbox.
Related endpoints
- POST /api/chat/runs — the headless counterpart: it provisions its own session + sandbox and runs without a streaming client. Use that for tasks/cron/programmatic runs; use this for interactive, streaming turns on an existing chat.
- GET /api/chat/{chatId}/stream — reconnect to an in-progress response.
- GET /api/chat/runs/{runId} — check whether a headless run finished (status, not content).
Authentication
x-api-key in header
Request
curl --request POST \
--url 'https://api.recoupable.dev/api/chat' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"messages": [
{
"id": "string",
"role": "user",
"content": "string"
}
],
"chatId": "YOUR_ID",
"sessionId": "YOUR_ID"
}'Request body required
Chat workflow request
application/json
messagesarray<UIMessage>requiredConversation history as an array of UIMessage objects, matching the Vercel AI SDK UIMessage shape. The last entry is typically the new user turn — the agent loop reads the entire array and persists the latest user message before the workflow starts.
Item properties for messages
idstringUnique identifier for the message
rolestring · enumThe role of the message sender
Values: "user", "assistant", "system"
contentstringThe text content of the message
chatIdstringrequiredUUID of an existing chat under sessionId. The chat's active_stream_id column is used for compare-and-set race control across concurrent requests.
format: uuid
sessionIdstringrequiredUUID of the session that owns both the chat and the sandbox. The session must have a non-null sandbox_state — call POST /api/sandbox first if not.
format: uuid
contextobjectOptional per-prompt context overrides forwarded into the agent loop.
Properties for context
contextLimitintegerUpper bound on the number of tokens / messages of conversation history the agent should consider for this prompt. Overrides the model's default for this call only.
Responses
200Server-Sent Events stream of UI message parts compatible with the Vercel AI SDK createUIMessageStreamResponse. The x-workflow-run-id response header carries the durable workflow run ID for resume/stop operations.+
x-workflow-run-idresponse headerDurable workflow run ID. Persist alongside the chat so a later GET /api/chat/{chatId}/stream request can resume this run after a disconnect.
text/event-stream
string
400Bad request — missing required fields, invalid body, or sandbox not initialized for the session.+
application/json
statusstring · enumrequiredStatus of the request
Values: "error"
errorstringrequiredError message describing what went wrong
missing_fieldsarray<string>Present on validation failures (400): the path of the field that failed. Empty for a malformed path parameter.
Item properties for missing_fields
string
401Missing or invalid API key.+
application/json
statusstring · enumrequiredStatus of the request
Values: "error"
errorstringrequiredError message describing what went wrong
missing_fieldsarray<string>Present on validation failures (400): the path of the field that failed. Empty for a malformed path parameter.
Item properties for missing_fields
string
403The API key's account does not own the referenced sessionId or chatId.+
application/json
statusstring · enumrequiredStatus of the request
Values: "error"
errorstringrequiredError message describing what went wrong
missing_fieldsarray<string>Present on validation failures (400): the path of the field that failed. Empty for a malformed path parameter.
Item properties for missing_fields
string
404Session or chat not found.+
application/json
statusstring · enumrequiredStatus of the request
Values: "error"
errorstringrequiredError message describing what went wrong
missing_fieldsarray<string>Present on validation failures (400): the path of the field that failed. Empty for a malformed path parameter.
Item properties for missing_fields
string
409A response is already streaming for this chat. Reconnect to it via GET /api/chat/{chatId}/stream instead of POSTing again.+
application/json
statusstring · enumrequiredStatus of the request
Values: "error"
errorstringrequiredError message describing what went wrong
missing_fieldsarray<string>Present on validation failures (400): the path of the field that failed. Empty for a malformed path parameter.
Item properties for missing_fields
string
Full specification
Download the OpenAPI file for complete schemas, constraints, and examples.
Download research.jsonView operation source
{
"summary": "Stream sandbox-driven chat (Vercel Workflow)",
"description": "Streams an agent loop running as a durable [Vercel Workflow](https://vercel.com/docs/workflow) against the session's sandbox. The agent uses sandbox-only tools (`bash`, `read`, `write`, `grep`, `glob`, `todo`, `task`, `ask_user_question`, `skill`, `fetch`) — no MCP or Composio. Requires a sandbox provisioned via [`POST /api/sandbox`](/api-reference/sandbox/create).\n\n**Related endpoints**\n- [`POST /api/chat/runs`](/api-reference/chat/runs) — the **headless** counterpart: it provisions its own session + sandbox and runs without a streaming client. Use that for tasks/cron/programmatic runs; use this for **interactive, streaming** turns on an existing chat.\n- [`GET /api/chat/{chatId}/stream`](/api-reference/chat/workflow-stream) — reconnect to an in-progress response.\n- [`GET /api/chat/runs/{runId}`](/api-reference/chat/runs-status) — check **whether** a headless run finished (status, not content).",
"security": [
{
"apiKeyAuth": []
}
],
"requestBody": {
"description": "Chat workflow request",
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatWorkflowRequest"
}
}
}
},
"responses": {
"200": {
"description": "Server-Sent Events stream of UI message parts compatible with the Vercel AI SDK `createUIMessageStreamResponse`. The `x-workflow-run-id` response header carries the durable workflow run ID for resume/stop operations.",
"headers": {
"x-workflow-run-id": {
"description": "Durable workflow run ID. Persist alongside the chat so a later `GET /api/chat/{chatId}/stream` request can resume this run after a disconnect.",
"schema": {
"type": "string"
}
}
},
"content": {
"text/event-stream": {
"schema": {
"type": "string",
"description": "Server-Sent Events stream containing UI message parts"
}
}
}
},
"400": {
"description": "Bad request — missing required fields, invalid body, or sandbox not initialized for the session.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatStreamErrorResponse"
}
}
}
},
"401": {
"description": "Missing or invalid API key.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatStreamErrorResponse"
}
}
}
},
"403": {
"description": "The API key's account does not own the referenced `sessionId` or `chatId`.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatStreamErrorResponse"
}
}
}
},
"404": {
"description": "Session or chat not found.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatStreamErrorResponse"
}
}
}
},
"409": {
"description": "A response is already streaming for this chat. Reconnect to it via `GET /api/chat/{chatId}/stream` instead of POSTing again.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ChatStreamErrorResponse"
}
}
}
}
}
}