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"
}'Try it
Fill in the fields, send the request from your browser, and read the live response. The curl below updates as you type.
Kept in this browser tab only and cleared when it closes.
curl --request POST \
--url 'https://recoup-api.vercel.app/api/chat' \
--header 'Content-Type: application/json' \
--data '{
"messages": [
{
"id": "string",
"role": "user",
"content": "string"
}
],
"chatId": "YOUR_ID",
"sessionId": "YOUR_ID"
}'Run this one from the terminal with the curl above.
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"
}
}
}
}
}
}