{"openapi":"3.1.0","info":{"title":"Blockbrain | Agentic","version":"canary","description":"API documentation for the Agentic API service."},"paths":{"/v1/api/memory/threads/{threadId}/messages":{"get":{"tags":["Memory"],"description":"Retrieve messages from a specific thread (last 50 messages)","responses":{"200":{"description":"Messages retrieved successfully (returns empty arrays if thread not found)"},"401":{"description":"Unauthorized - missing or invalid access token"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"post":{"tags":["Memory"],"description":"Save messages to a specific thread","responses":{"200":{"description":"Messages saved successfully"},"400":{"description":"Bad request - invalid body"},"401":{"description":"Unauthorized - missing or invalid access token"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"delete":{"tags":["Memory"],"description":"Delete all messages from a specific thread","responses":{"200":{"description":"All messages deleted successfully"},"401":{"description":"Unauthorized - missing or invalid access token"},"404":{"description":"Thread not found or user does not have access"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/{threadId}/messages/find":{"get":{"tags":["Memory"],"description":"Find messages in a thread by metadata fields or text content","responses":{"200":{"description":"Matching messages returned (empty array if none found)"},"401":{"description":"Unauthorized - missing or invalid access token"},"404":{"description":"Thread not found or user does not have access"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/{threadId}/messages/{messageId}":{"delete":{"tags":["Memory"],"description":"Delete a specific message from a thread","responses":{"200":{"description":"Message deleted successfully"},"401":{"description":"Unauthorized - missing or invalid access token"},"404":{"description":"Thread not found or user does not have access"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"patch":{"tags":["Memory"],"description":"Update the content of a specific message in a thread","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"parts":{"type":"array","description":"The message parts (text, images, files, etc.)","items":{"type":"object","description":"A message part. Must include a \"type\" field. Shape varies by type (e.g. text, reasoning, tool-invocation, image, file).","additionalProperties":true,"required":["type"]}}},"required":["parts"]}}}},"responses":{"200":{"description":"Message updated successfully"},"400":{"description":"Bad request - invalid body"},"401":{"description":"Unauthorized - missing or invalid access token"},"404":{"description":"Resource not found. Possible causes: thread does not exist, user does not have access to the thread, message does not exist, or message does not belong to the specified thread."},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/agents":{"get":{"tags":["Agents"],"description":"Retrieve list of available agents with their capabilities","parameters":[{"name":"name","in":"query","description":"Filter agents by name (case-insensitive substring match)","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Agents retrieved successfully"},"401":{"description":"Unauthorized - missing or invalid access token"},"500":{"description":"Internal server error"}}},"servers":[{"url":"/"}]},"/v1/api/models":{"get":{"tags":["Models"],"description":"List all available models with their display metadata","responses":{"200":{"description":"List of available models","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"displayName":{"type":"string"},"mode":{"type":"string"},"supportsToolUse":{"type":"boolean"},"supportsVision":{"type":"boolean"},"maxInputTokens":{"type":"number"},"maxOutputTokens":{"type":"number"},"tags":{"type":"array","items":{"type":"string"}}}}},"total":{"type":"number"}}}}}}}},"servers":[{"url":"/"}]},"/v1/api/presentation/templates/{templateId}/preview":{"get":{"tags":["Presentation"],"description":"Get SVG previews for a presentation template. Resolves built-in templates first, then falls back to the calling user's uploaded templates — the local Postgres registry, or the integrations template API when `agent.template-api-loader` is ON.","parameters":[{"name":"templateId","in":"path","required":true,"schema":{"type":"string"},"description":"Template identifier (e.g., \"mckinsey\", \"google_style\", or a user-uploaded slug)"}],"responses":{"200":{"description":"Template SVG previews"},"401":{"description":"Unauthorized"},"404":{"description":"Template not found or not enabled for this organization"}}},"servers":[{"url":"/"}]},"/v1/api/presentation/templates/pipeline":{"post":{"tags":["Templates"],"description":"Start the template pipeline for a PPTX file. The endpoint validates the request, acknowledges with 202, and runs the (multi-minute) pipeline asynchronously — it downloads the file from the internal media-server via M2M, builds the template, and uploads the bundle. `templateMode` selects the pipeline: `mirror` (default) reproduces the slides verbatim; `fidelity` templatizes them into reusable layouts. Completion or failure is reported back to integrations via the internal pipeline callback keyed by jobId; this endpoint does NOT return the pipeline result inline.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"fileKey":{"type":"string","description":"Media-server key of the uploaded PPTX file"},"displayName":{"type":"string"},"category":{"type":"string","enum":["brand","general","scenario","government","special"]},"sourceName":{"type":"string"},"orgId":{"type":"string","description":"Organization ID for M2M download (X-Acting-As-Org-Id)"},"userId":{"type":"string","description":"User ID for M2M download (X-Acting-As-User-Id)"},"jobId":{"type":"string","description":"Integrations upload-job id; echoed to the completion callback for correlation"},"templateMode":{"type":"string","enum":["fidelity","mirror"],"description":"Pipeline selector; defaults to 'mirror' when omitted."}},"required":["fileKey","displayName","category","sourceName","orgId","userId","jobId"]}}}},"responses":{"202":{"description":"Pipeline accepted — runs asynchronously; result delivered via the integrations callback"},"400":{"description":"Missing or invalid fields"},"401":{"description":"Missing or invalid Zitadel bearer token"},"500":{"description":"Failed to accept the pipeline request"}}},"servers":[{"url":"/"}]},"/v1/api/custom-agents/{customAgentId}/export":{"post":{"tags":["Custom Agents"],"description":"Export a custom agent as a GitHub issue for conversion to predefined agent","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Reason for exporting this custom agent. Must be a string holding at least one readable character. It is rendered into the GitHub issue as a literal code block, so Markdown and HTML are shown as written rather than interpreted. Invisible and bidirectional-override characters are stripped."}},"required":["reason"]}}}},"responses":{"200":{"description":"Custom agent exported successfully as GitHub issue"},"400":{"description":"Invalid request body. Returned when reason is absent, is not a string, or holds no readable text once invisible characters are stripped."},"401":{"description":"Unauthorized - missing or invalid access token"},"403":{"description":"Forbidden - export is restricted to Blockbrain-internal callers when the agent.restrict-custom-agent-export kill switch is enabled"},"500":{"description":"Internal server error"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads":{"post":{"tags":["Memory"],"description":"Create a new conversation thread with optional custom ID","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"threadId":{"type":"string","description":"Optional custom ID for the thread (auto-generated if not provided)"},"botId":{"type":"string","description":"The ID of the bot/agent associated with this thread"},"dataRoomId":{"type":"string","description":"Optional ID of the data room to associate with this thread"}},"required":["botId"]}}}},"responses":{"201":{"description":"Thread created successfully"},"400":{"description":"Invalid request body or missing required fields"},"401":{"description":"Unauthorized - missing or invalid access token"},"409":{"description":"Thread with specified ID already exists"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/{threadId}":{"get":{"tags":["Memory"],"description":"Retrieve a specific thread by ID","responses":{"200":{"description":"Thread retrieved successfully (returns null if not found or unauthorized)"},"401":{"description":"Unauthorized - missing or invalid access token"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/{threadId}/usage":{"get":{"tags":["Memory"],"description":"Retrieve token usage and remaining context tokens from the last step of a thread","parameters":[{"name":"agentId","in":"query","required":false,"schema":{"type":"string"},"description":"Agent ID. Used to resolve the model context limit for new threads. For agents with a static model (e.g. Slack agent), the model is resolved automatically. For the custom agent, also pass modelGroup."},{"name":"modelGroup","in":"query","required":false,"schema":{"type":"string"},"description":"LiteLLM model group override. Required for custom-agent where the model is bot-specific (e.g. \"azure-gpt-41\"). Takes precedence over the model resolved from agentId."}],"responses":{"200":{"description":"Usage retrieved successfully (returns null fields if no usage data found)"},"401":{"description":"Unauthorized - missing or invalid access token"},"404":{"description":"Thread not found or does not belong to the user"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/{threadId}/working-memory":{"get":{"tags":["Memory"],"description":"Retrieve working memory for a specific thread","responses":{"200":{"description":"Working memory retrieved successfully"},"401":{"description":"Unauthorized - missing or invalid access token"},"404":{"description":"Thread or working memory not found"},"503":{"description":"Memory service temporarily unavailable"}}},"put":{"tags":["Memory"],"description":"Set or update the working memory for a specific thread","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"workingMemory":{"type":"string","description":"The working memory content to set for the thread"}},"required":["workingMemory"]}}}},"responses":{"200":{"description":"Working memory updated successfully"},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - missing or invalid access token"},"404":{"description":"Thread not found"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/{threadId}/tool-approval-mode":{"patch":{"tags":["Memory"],"summary":"Set the tool-approval mode for a thread (PDEV-4694)","description":"Persist `metadata.toolApprovalMode` on the chat thread. When 'auto', the streaming runtime strips the require-approval marker for write tools in this thread. The mode is honoured only when the `agent.tool-approval-overrides` rollout flag is enabled and the global kill-switch `botticelli.tool-approval.enable` permits the runtime gate.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["auto","manual"],"description":"Approval mode: `auto` skips the dialog, `manual` keeps the per-tool default."}},"required":["mode"]}}}},"responses":{"200":{"description":"Mode updated"},"400":{"description":"Invalid mode value"},"401":{"description":"Unauthorized"},"404":{"description":"Thread not found"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/{threadId}/data-retention":{"delete":{"tags":["Memory"],"description":"Delete a thread for data retention purposes (requires secret key authorization)","x-auth-bypass":true,"responses":{"200":{"description":"Thread deleted successfully"},"401":{"description":"Unauthorized - missing access token"},"403":{"description":"Forbidden - invalid secret key"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/delete-multiple":{"post":{"tags":["Memory"],"description":"Delete multiple threads by their IDs (only threads owned by the user)","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"threadIds":{"type":"array","items":{"type":"string"},"description":"Array of thread IDs to delete"}},"required":["threadIds"]}}}},"responses":{"200":{"description":"Threads deleted successfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"deletedCount":{"type":"number","description":"Number of the requested threads that belonged to the caller and were deleted. Zero means none of the ids matched one of their threads."}}}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - missing or invalid access token"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/bot/{botId}":{"delete":{"tags":["Memory"],"description":"Delete all threads associated with a specific bot for the current user","responses":{"200":{"description":"Threads deleted successfully"},"401":{"description":"Unauthorized - missing or invalid access token"},"409":{"description":"Database constraint violation"},"500":{"description":"Internal server error"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/data-room/{dataRoomId}":{"delete":{"tags":["Memory"],"description":"Delete all threads associated with a specific data room for the current user","responses":{"200":{"description":"Threads deleted successfully"},"401":{"description":"Unauthorized - missing or invalid access token"},"409":{"description":"Database constraint violation"},"500":{"description":"Internal server error"}}},"servers":[{"url":"/"}]},"/healthz":{"get":{"tags":["System"],"description":"Health check endpoint to verify service availability","x-auth-bypass":true,"responses":{"200":{"description":"Service is healthy"}}},"servers":[{"url":"/"}]},"/v1/api/agents/tools":{"get":{"tags":["Agents"],"description":"Retrieve list of all available tools for agents","responses":{"200":{"description":"Tools retrieved successfully"},"401":{"description":"Unauthorized - missing or invalid access token"},"500":{"description":"Internal server error"}}},"servers":[{"url":"/"}]},"/v1/api/agents/follow-up/{threadId}":{"get":{"tags":["Agents"],"description":"Retrieve follow-up questions based on a specific thread ID","parameters":[{"name":"threadId","in":"path","required":true,"schema":{"type":"string"},"description":"The ID of the thread to analyze"}],"responses":{"200":{"description":"Follow-up questions generated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"questions":{"type":"array","items":{"type":"string"}}}}}}},"401":{"description":"Unauthorized - missing or invalid access token"},"404":{"description":"Thread not found"},"500":{"description":"Internal server error"}}},"servers":[{"url":"/"}]},"/v1/api/memory/threads/export":{"post":{"tags":["Memory"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"convo_id":{"type":"string"},"file_type":{"type":"string"},"time_zone":{"type":"number"},"title":{"type":"string"}},"required":["convo_id","file_type","time_zone"]}}}},"responses":{"200":{"description":"PDF generated, uploaded, and session created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"fileUrl":{"type":"string","description":"Signed media session URL for the PDF"},"filename":{"type":"string","description":"Filename of the generated PDF"}},"required":["fileUrl","filename"]},"examples":{"success":{"value":{"fileUrl":"https://media-server.local/media/<sessionId>","filename":"Exported_Thread.pdf"}}}}}},"400":{"description":"Thread not found or empty","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"number"},"key":{"type":"string"},"body":{"type":"string"}},"required":["code","key","body"]},"examples":{"threadNotFound":{"value":{"code":400,"key":"agentic-thread-not-found","body":"Oops! Looks like the conversation is no longer available or empty"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"examples":{"unauthorized":{"value":{"error":"Unauthorized"}}}}}},"500":{"description":"Internal server error (PDF generation, upload, or session creation failed)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"examples":{"uploadFailed":{"value":{"error":"Failed to upload PDF"}},"sessionFailed":{"value":{"error":"Failed to create media session"}},"generic":{"value":{"error":"Internal server error"}}}}}}}},"servers":[{"url":"/"}]},"/v1/api/memory/messages/export":{"post":{"tags":["Memory"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"message_id":{"type":"string"},"file_type":{"type":"string"},"time_zone":{"type":"number"},"bot_id":{"type":"string"}},"required":["message_id","file_type","time_zone"]}}}}},"servers":[{"url":"/"}]},"/v1/api/media/snapshots":{"post":{"tags":["Media"],"summary":"Upload a chart/diagram snapshot PNG for export embedding","description":"Stores a client-rendered chart/diagram PNG under its tool-allocated snapshot key so PDF/DOCX exports can embed it. The storage tenant is derived from the authenticated acting organization.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"PNG snapshot (max 10 MB)"},"key":{"type":"string","description":"Snapshot key: botticelli/(chart|diagram)-snapshot/<toolCallId>"}},"required":["file","key"]}}}},"responses":{"200":{"description":"Snapshot stored","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string"}},"required":["key"]}}}},"400":{"description":"Missing/invalid field, invalid snapshot key, or non-PNG content"},"401":{"description":"Missing or unresolved authentication context"},"413":{"description":"Snapshot exceeds the 10 MB limit"},"502":{"description":"Media-server upload failed"}}},"servers":[{"url":"/"}]},"/v1/api/memory/messages/metadata":{"put":{"tags":["Memory"],"description":"Update metadata for multiple messages in bulk","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The message ID to update"},"metadata":{"type":"object","description":"The metadata to update for the message"}},"required":["id"]},"description":"Array of messages with their IDs and metadata to update"}},"required":["messages"]}}}},"responses":{"200":{"description":"Messages metadata updated successfully"},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized - missing or invalid access token"},"500":{"description":"Internal server error"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/agents/{agentId}/stream":{"post":{"deprecated":true,"tags":["Agents"],"summary":"Stream agent chat (deprecated)","description":"Deprecated event-based SSE stream. Emits named SSE events (`event: <type>\\ndata: <json>\\n\\n`). To run the agent inside a data room and let it reach the knowledge bases connected to that data room, send the `x-blockbrain-active-bot-id` and `x-blockbrain-data-room-id` headers. Without them the call still succeeds, but the agent answers as if no knowledge base were connected. This endpoint is frozen — migrate to POST /v2/api/agents/:agentId/stream (AI SDK v6 UIMessage format, suspend/resume, tool-call approval).","parameters":[{"name":"agentId","in":"path","required":true,"description":"The ID of the agent to chat with","schema":{"type":"string"}},{"name":"x-tool-ids","in":"header","required":false,"description":"Comma-separated list of tool IDs to enable for this request. Used by non-Blocky clients (e.g., Eddy) that do not send x-blockbrain-data-room-id. When present, the agent is restricted to these tools. Omitting the header applies no filter, so the agent keeps whatever its capability defaults resolved. Listing a tool the agent does not already have does not grant it.","schema":{"type":"string"}},{"name":"x-blockbrain-active-bot-id","in":"header","required":false,"description":"Active bot id. `POST /cortex/active-bot/{botId}/convo` on blocky returns `{ dataRoomId, activeBotId }` in one response, so a single call yields both headers. That call creates a new data room, so use it only when starting a fresh conversation. That call accepts a predefined bot id and fetches or creates the active bot behind it, so read `activeBotId` from its response body rather than reusing the path id. For a data room that already exists, its active bot id is the room's `data[].botId` in `GET /cortex/active-bot/{activeBotId}/dataroom`, whose own path id is already an active bot id and not a predefined one. Send it together with `x-blockbrain-data-room-id` to run the agent inside a data room and reach the knowledge bases connected to it. Omit both only for non-Blocky clients that select their tools with `x-tool-ids`. If either header is missing the request still succeeds. The agent runs with a minimal context, has no data room, and cannot reach the knowledge bases connected to it, so it answers as if no knowledge base were connected. Nothing fails and no error is returned, which is what makes an omitted header hard to spot.","schema":{"type":"string"}},{"name":"x-blockbrain-data-room-id","in":"header","required":false,"description":"Conversation id, despite the header name. Blocky fills its `dataRoomId` response field with the conversation id, and this service resolves the header through `GET /cortex/conversation/{id}`. `POST /cortex/active-bot/{botId}/convo` on blocky returns `{ dataRoomId, activeBotId }` in one response, so a single call yields both headers. That call creates a new data room, so use it only when starting a fresh conversation. To stream into a data room that already exists, take `data[].convos[]._id` from `GET /cortex/active-bot/{activeBotId}/dataroom` (pinned rooms sit under `pinedDatarooms[]` on the same response). Do not send that response's `data[]._id`: that is the data room entity id on the same element, and this header does not resolve it. Send it together with `x-blockbrain-active-bot-id` to run the agent inside a data room and reach the knowledge bases connected to it. Omit both only for non-Blocky clients that select their tools with `x-tool-ids`. If either header is missing the request still succeeds. The agent runs with a minimal context, has no data room, and cannot reach the knowledge bases connected to it, so it answers as if no knowledge base were connected. Nothing fails and no error is returned, which is what makes an omitted header hard to spot.","schema":{"type":"string"}},{"name":"x-blockbrain-organization-id","in":"header","required":false,"description":"Accepted for backward compatibility and ignored. The organization is taken from the access token, and `x-zitadel-org-id` selects it when the token carries roles for more than one. Sending this header changes nothing about organization, data room, or knowledge base resolution.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object"},"description":"Conversation messages"},"threadId":{"type":"string","description":"Thread ID for memory persistence"},"resourceId":{"type":"string","description":"Resource (user) ID associated with the thread"}},"required":["messages"]}}}},"responses":{"200":{"description":"Named SSE event stream (`event: <type>\\ndata: <json>\\n\\n`)","content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"404":{"description":"Agent not found"},"429":{"description":"Usage limit reached for the current period","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["usage_limit_reached"]},"message":{"type":"string"},"used":{"type":"number"},"limit":{"type":"number"},"timeframe":{"type":"string","enum":["monthly","weekly","daily"]},"periodResetsAt":{"type":"string","format":"date-time"}},"required":["error","message","used","limit","timeframe","periodResetsAt"]}}}}}},"servers":[{"url":"/"}]},"/v2/api/agents/{agentId}/stream":{"post":{"tags":["Agents"],"summary":"Stream agent chat","description":"Send messages to an agent and stream the response using AI SDK v6 UIMessage format. Supports suspend/resume for tool-call approval (human-in-the-loop). Thread/memory association is done via the `memory` field. To run the agent inside a data room and let it reach the knowledge bases connected to that data room, send the `x-blockbrain-active-bot-id` and `x-blockbrain-data-room-id` headers. Without them the call still succeeds, but the agent answers as if no knowledge base were connected. For the deprecated event-based SSE format, use POST /v1/api/agents/:agentId/stream.","parameters":[{"name":"agentId","in":"path","required":true,"description":"The ID of the agent to chat with","schema":{"type":"string"}},{"name":"x-tool-ids","in":"header","required":false,"description":"Comma-separated list of tool IDs to enable for this request. Used by non-Blocky clients (e.g., Eddy) that do not send x-blockbrain-data-room-id. When present, the agent is restricted to these tools. Omitting the header applies no filter, so the agent keeps whatever its capability defaults resolved. Listing a tool the agent does not already have does not grant it.","schema":{"type":"string"}},{"name":"x-blockbrain-active-bot-id","in":"header","required":false,"description":"Active bot id. `POST /cortex/active-bot/{botId}/convo` on blocky returns `{ dataRoomId, activeBotId }` in one response, so a single call yields both headers. That call creates a new data room, so use it only when starting a fresh conversation. That call accepts a predefined bot id and fetches or creates the active bot behind it, so read `activeBotId` from its response body rather than reusing the path id. For a data room that already exists, its active bot id is the room's `data[].botId` in `GET /cortex/active-bot/{activeBotId}/dataroom`, whose own path id is already an active bot id and not a predefined one. Send it together with `x-blockbrain-data-room-id` to run the agent inside a data room and reach the knowledge bases connected to it. Omit both only for non-Blocky clients that select their tools with `x-tool-ids`. If either header is missing the request still succeeds. The agent runs with a minimal context, has no data room, and cannot reach the knowledge bases connected to it, so it answers as if no knowledge base were connected. Nothing fails and no error is returned, which is what makes an omitted header hard to spot.","schema":{"type":"string"}},{"name":"x-blockbrain-data-room-id","in":"header","required":false,"description":"Conversation id, despite the header name. Blocky fills its `dataRoomId` response field with the conversation id, and this service resolves the header through `GET /cortex/conversation/{id}`. `POST /cortex/active-bot/{botId}/convo` on blocky returns `{ dataRoomId, activeBotId }` in one response, so a single call yields both headers. That call creates a new data room, so use it only when starting a fresh conversation. To stream into a data room that already exists, take `data[].convos[]._id` from `GET /cortex/active-bot/{activeBotId}/dataroom` (pinned rooms sit under `pinedDatarooms[]` on the same response). Do not send that response's `data[]._id`: that is the data room entity id on the same element, and this header does not resolve it. Send it together with `x-blockbrain-active-bot-id` to run the agent inside a data room and reach the knowledge bases connected to it. Omit both only for non-Blocky clients that select their tools with `x-tool-ids`. If either header is missing the request still succeeds. The agent runs with a minimal context, has no data room, and cannot reach the knowledge bases connected to it, so it answers as if no knowledge base were connected. Nothing fails and no error is returned, which is what makes an omitted header hard to spot.","schema":{"type":"string"}},{"name":"x-blockbrain-organization-id","in":"header","required":false,"description":"Accepted for backward compatibility and ignored. The organization is taken from the access token, and `x-zitadel-org-id` selects it when the token carries roles for more than one. Sending this header changes nothing about organization, data room, or knowledge base resolution.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","description":"Array of UIMessages (AI SDK v6 format).","items":{"type":"object"}},"resumeData":{"type":"object","description":"Resume payload for a suspended tool-call approval."},"runId":{"type":"string","description":"Run ID required when resuming a suspended execution."},"trigger":{"type":"string","enum":["submit-message","regenerate-message"],"description":"Client action trigger; regenerate re-runs the last assistant message."},"memory":{"type":"object","description":"Memory configuration for thread persistence. Pass `thread` (thread ID or object with `id`) and `resource` (user/resource ID) to associate this run with a stored conversation thread. Without this field, messages are not persisted.","properties":{"thread":{"description":"Thread identifier. Either a plain ID string or an object containing `id`.","anyOf":[{"type":"string","description":"Thread ID string"},{"type":"object","properties":{"id":{"type":"string"}},"required":["id"],"additionalProperties":true,"description":"Thread object (at minimum `{ id: string }`)"}]},"resource":{"type":"string","description":"Resource (user) ID associated with this thread."},"options":{"type":"object","additionalProperties":{},"description":"Advanced memory configuration options (e.g. semantic search settings)."},"readOnly":{"type":"boolean","description":"When true, memory is read but not written during this run."}},"required":["thread","resource"],"additionalProperties":false},"instructions":{"description":"Additional system instructions to inject for this run.","anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"pageContext":{"type":"string","description":"PDEV-7395 (EVORIS): opaque host-page context set by the embedding platform (a short summary of the app/data on the user's screen). Prepended as a per-run system message so the agent answers with awareness of the page. Additive to the agent prompt (does not replace it) and not persisted, so the host can send a fresh value each turn. Length-capped server-side."},"maxSteps":{"type":"number","description":"Maximum number of agentic tool-call steps before the run is halted."},"activeTools":{"type":"array","items":{"type":"string"},"description":"Allowlist of tool names the agent may use in this run."},"externalTools":{"type":"array","description":"External tool definitions for relay execution. Tools defined here are presented to the model as native tools. The suspend/relay is honored only by the WebComponent Agent: when its model calls one, the stream suspends and emits a data-tool-call-suspended event, the client executes the tool locally and resumes with resumeData + runId. The resume request must re-send this externalTools array (the relay tool is rebuilt per request). For other agents the tools are exposed to the model but calls are not executed or relayed.","items":{"type":"object","required":["name","description","parameters"],"properties":{"name":{"type":"string","description":"Unique tool name"},"description":{"type":"string","description":"What the tool does (shown to the model)"},"parameters":{"type":"object","description":"JSON Schema describing the tool input"}}}},"toolChoice":{"description":"How the agent selects tools.","anyOf":[{"type":"string","enum":["auto","none","required"]},{"type":"object","properties":{"type":{"type":"string","enum":["tool"]},"toolName":{"type":"string"}},"required":["type","toolName"],"additionalProperties":false}]},"requireToolApproval":{"type":"boolean","description":"When true, tool calls are suspended and a `data-tool-call-approval` event is emitted. Resume with `resumeData` + `runId`."},"providerOptions":{"type":"object","description":"Provider-specific options (e.g. Anthropic thinking budget).","properties":{"anthropic":{"type":"object","additionalProperties":{}},"google":{"type":"object","additionalProperties":{}},"openai":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"tracingOptions":{"type":"object","description":"OpenTelemetry tracing metadata for this run.","properties":{"metadata":{"type":"object","additionalProperties":{}},"traceId":{"type":"string"},"parentSpanId":{"type":"string"}},"additionalProperties":false},"output":{"description":"Expected output schema (structured output)."}}}}}},"responses":{"200":{"description":"Streaming response from the agent","content":{"text/event-stream":{"schema":{"type":"string","description":"AI SDK v6 UIMessage chunk SSE stream"}}}},"400":{"description":"Bad request — invalid input"},"404":{"description":"Agent not found"},"429":{"description":"Usage limit reached for the current period","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","enum":["usage_limit_reached"]},"message":{"type":"string"},"used":{"type":"number"},"limit":{"type":"number"},"timeframe":{"type":"string","enum":["monthly","weekly","daily"]},"periodResetsAt":{"type":"string","format":"date-time"}},"required":["error","message","used","limit","timeframe","periodResetsAt"]}}}}}},"servers":[{"url":"/"}]},"/v2/api/runs/{runId}/cancel":{"post":{"tags":["Agents"],"summary":"Cancel an in-flight agent run","description":"Aborts the agent run identified by `runId`, wherever in the cluster it is executing. Only the principal that started the run may cancel it. Idempotent: cancelling a run that has already finished or never existed is accepted and has no effect.","parameters":[{"name":"runId","in":"path","required":true,"description":"The run id the client sent on the stream request.","schema":{"type":"string"}}],"responses":{"200":{"description":"The run was held by this instance and has been aborted."},"202":{"description":"Cancellation was published to the instance holding the run."},"401":{"description":"No authenticated principal."},"403":{"description":"The run belongs to another principal."},"404":{"description":"The chat.agent-run-cancel rollout is off for this tenant."},"503":{"description":"The run is not local and the cancellation bus is unreachable."}}},"servers":[{"url":"/"}]},"/v2/api/audio/transcriptions":{"post":{"tags":["Audio"],"summary":"Transcribe audio (speech-to-text)","description":"OpenAI-compatible /v1/audio/transcriptions proxy. Accepts a multipart audio file plus a LiteLLM model name; returns OpenAI-shaped JSON `{ text }`.","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["file","model"],"properties":{"file":{"type":"string","format":"binary"},"model":{"type":"string","description":"LiteLLM STT model id (e.g. whisper-1, azure/whisper)"},"language":{"type":"string","description":"ISO-639 hint"},"prompt":{"type":"string","description":"Biasing prompt for Whisper-class models"},"temperature":{"type":"number","description":"0.0–1.0"}}}}}},"responses":{"200":{"description":"OpenAI-compatible transcription response","content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"]}}}},"400":{"description":"Bad request — missing file/model or unsupported filetype"},"413":{"description":"Audio exceeds the 25 MB cap"},"502":{"description":"LiteLLM upstream failure"}}},"servers":[{"url":"/"}]},"/v2/api/audio/speech":{"post":{"tags":["Audio"],"summary":"Synthesize speech (text-to-speech)","description":"OpenAI-compatible /v1/audio/speech proxy. JSON in, audio bytes out. The `response_format` field controls the audio container; the response Content-Type is set to match (defaults to audio/mpeg for mp3).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["model","input"],"properties":{"model":{"type":"string","description":"LiteLLM TTS model id (e.g. tts-1, azure/tts-1-hd)"},"input":{"type":"string","description":"Text to synthesize. Capped at 4000 chars."},"voice":{"type":"string","description":"Voice id required by OpenAI-style models"},"response_format":{"type":"string","enum":["mp3","opus","aac","flac","wav","pcm"]},"speed":{"type":"number","description":"0.25–4.0"}}}}}},"responses":{"200":{"description":"Streamed audio bytes","content":{"audio/mpeg":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Bad request — missing/invalid input"},"502":{"description":"LiteLLM upstream failure"}}},"servers":[{"url":"/"}]},"/v2/api/audio/models":{"get":{"tags":["Audio"],"summary":"List audio (STT/TTS) models","description":"Returns LiteLLM models filtered by audio capability. Use ?mode=audio_transcription or ?mode=audio_speech.","parameters":[{"name":"mode","in":"query","required":false,"schema":{"type":"string","enum":["audio_transcription","audio_speech"]},"description":"Filter to a specific audio mode. When omitted, both transcription and speech models are returned."}],"responses":{"200":{"description":"Filtered model list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"mode":{"type":"string"},"provider":{"type":"string"}}}},"total":{"type":"number"}}}}}},"400":{"description":"Invalid mode parameter"}}},"servers":[{"url":"/"}]},"/v2/api/threads":{"get":{"tags":["DynamicChat"],"summary":"List chat threads","description":"List the current user's threads for the agent-first chat product.","responses":{"200":{"description":"Thread list","content":{"application/json":{"schema":{"type":"array","items":{"type":"object"}}}}},"401":{"description":"Unauthorized"},"503":{"description":"Memory service temporarily unavailable"}}},"post":{"tags":["DynamicChat"],"summary":"Create a chat thread","description":"Create a new thread for the agent-first chat product. `agentId` is a workspace agent UUID; omit to fall back to the hardcoded default.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"agentId":{"type":"string","format":"uuid","description":"Workspace agent UUID. When set, the stream resolves prompt + model from workspace at request time. Omit to use hardcoded defaults."},"title":{"type":"string","description":"Optional human-readable thread title."}}}}}},"responses":{"201":{"description":"Thread created"},"401":{"description":"Unauthorized"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v2/api/threads/{threadId}":{"get":{"tags":["DynamicChat"],"summary":"Get a chat thread","parameters":[{"name":"threadId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Thread"},"401":{"description":"Unauthorized"},"404":{"description":"Thread not found"},"503":{"description":"Memory service temporarily unavailable"}}},"delete":{"tags":["DynamicChat"],"summary":"Delete a chat thread","parameters":[{"name":"threadId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Thread deleted (or absent — response is idempotent)."},"401":{"description":"Unauthorized"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v2/api/threads/{threadId}/todos":{"get":{"tags":["DynamicChat"],"summary":"Get thread todo list","parameters":[{"name":"threadId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Todo list","content":{"application/json":{"schema":{"type":"object","properties":{"todos":{"type":"array","items":{"type":"object"}},"updatedAt":{"type":"string","nullable":true}}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Thread not found or feature not enabled"},"500":{"description":"Internal error"},"503":{"description":"Memory service unavailable"}}},"servers":[{"url":"/"}]},"/v2/api/threads/{threadId}/messages":{"get":{"tags":["DynamicChat"],"summary":"Get a thread message history","description":"Returns the last 50 messages of the thread in AI SDK v6 UIMessage format.","parameters":[{"name":"threadId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Thread message history","content":{"application/json":{"schema":{"type":"array","description":"AI SDK v6 UIMessage objects","items":{"type":"object"}}}}},"401":{"description":"Unauthorized"},"404":{"description":"Thread not found or not owned by caller"},"503":{"description":"Memory service temporarily unavailable"}}},"post":{"tags":["DynamicChat"],"summary":"Send a message and stream the agent response","description":"Streams the dynamic chat agent response as an AI SDK v6 UIMessage SSE stream. Thread ID comes from the path; resource (user) ID is derived from the JWT.","parameters":[{"name":"threadId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","description":"Array of UIMessages (AI SDK v6 format).","items":{"type":"object"}}}}}}},"responses":{"200":{"description":"Streaming response","content":{"text/event-stream":{"schema":{"type":"string","description":"AI SDK v6 UIMessage chunk SSE stream"}}}},"400":{"description":"Bad request — invalid body"},"401":{"description":"Unauthorized"},"404":{"description":"Thread not found or not owned by caller"},"500":{"description":"Internal error"}}},"servers":[{"url":"/"}]},"/v2/api/threads/{threadId}/agent":{"post":{"tags":["DynamicChat"],"summary":"Swap the workspace agent pinned to a thread","description":"Upserts `current_agent_id` on the thread's `workspace.thread_configs` row. Next turn on the thread streams using the new agent's prompt + model.","parameters":[{"name":"threadId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"agentId":{"type":"string","nullable":true,"format":"uuid","description":"Workspace agent UUID, or `null` to unpin and fall back to defaults."}}}}}},"responses":{"200":{"description":"Thread updated"},"400":{"description":"Invalid body"},"401":{"description":"Unauthorized"},"404":{"description":"Thread not found or not owned by caller"},"503":{"description":"Memory service temporarily unavailable"}}},"servers":[{"url":"/"}]},"/v1/api/supervisors/available-agents":{"get":{"tags":["Custom Supervisors"],"description":"List agent IDs available as sub-agents for custom supervisors","responses":{"200":{"description":"List of available agent IDs with display names"}}},"servers":[{"url":"/"}]},"/v1/api/supervisors":{"post":{"tags":["Custom Supervisors"],"description":"Create a new custom supervisor configuration","x-required-roles":["kb:admin","kb:superadmin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Display name for the supervisor"},"instructions":{"type":"string","description":"System prompt for the supervising LLM"},"model":{"type":"string","description":"LiteLLM model name (optional, uses default when omitted)"},"subAgentIds":{"type":"array","items":{"type":"string"},"description":"Agent IDs the supervisor is allowed to delegate to"}},"required":["name","instructions","subAgentIds"]}}}},"responses":{"201":{"description":"Custom supervisor created successfully"},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"}}},"get":{"tags":["Custom Supervisors"],"description":"List all custom supervisors for the authenticated organisation","x-required-roles":["kb:admin","kb:superadmin"],"responses":{"200":{"description":"List of custom supervisors"},"401":{"description":"Unauthorized"}}},"servers":[{"url":"/"}]},"/v1/api/supervisors/{id}":{"get":{"tags":["Custom Supervisors"],"description":"Get a single custom supervisor by ID","x-required-roles":["kb:admin","kb:superadmin"],"responses":{"200":{"description":"Custom supervisor details"},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}},"put":{"tags":["Custom Supervisors"],"description":"Update an existing custom supervisor configuration","x-required-roles":["kb:admin","kb:superadmin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"instructions":{"type":"string"},"model":{"type":"string"},"subAgentIds":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Updated custom supervisor"},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}},"delete":{"tags":["Custom Supervisors"],"description":"Delete a custom supervisor configuration","x-required-roles":["kb:admin","kb:superadmin"],"responses":{"200":{"description":"Deleted successfully"},"401":{"description":"Unauthorized"},"404":{"description":"Not found"}}},"servers":[{"url":"/"}]},"/v1/api/scheduled-messages":{"post":{"tags":["Scheduled Messages"],"description":"Create a new scheduled message for automated chat execution","x-required-roles":["kb:admin","kb:superadmin"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"threadId":{"type":"string","description":"Thread ID to send messages to"},"agentId":{"type":"string","description":"Agent ID to use for processing"},"message":{"type":"string","description":"Message content to send","maxLength":10000},"intervalPreset":{"type":"string","description":"Predefined interval or \"custom\" for custom cron","enum":["every_15_minutes","every_30_minutes","every_hour","every_2_hours","every_6_hours","every_12_hours","daily_9am","daily_noon","daily_6pm","weekly_monday_9am","custom"]},"customCron":{"type":"string","description":"Custom cron expression (required when intervalPreset is \"custom\")"},"metadata":{"type":"object","description":"Additional metadata (botId, dataRoomId, networkId, displayName)"}},"required":["threadId","agentId","message","intervalPreset"]}}}},"responses":{"201":{"description":"Scheduled message created successfully"},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"}}},"get":{"tags":["Scheduled Messages"],"description":"List scheduled messages for the current user or a specific thread","x-required-roles":["kb:admin","kb:superadmin"],"parameters":[{"name":"threadId","in":"query","description":"Filter by thread ID","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"List of scheduled messages"},"401":{"description":"Unauthorized"}}},"servers":[{"url":"/"}]},"/v1/api/scheduled-messages/{id}":{"get":{"tags":["Scheduled Messages"],"description":"Get a specific scheduled message by ID","x-required-roles":["kb:admin","kb:superadmin"],"parameters":[{"name":"id","in":"path","description":"Scheduled message ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Scheduled message details"},"401":{"description":"Unauthorized"},"404":{"description":"Scheduled message not found"}}},"put":{"tags":["Scheduled Messages"],"description":"Update a scheduled message","x-required-roles":["kb:admin","kb:superadmin"],"parameters":[{"name":"id","in":"path","description":"Scheduled message ID","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Message content"},"intervalPreset":{"type":"string","description":"Interval preset"},"customCron":{"type":"string","description":"Custom cron expression"},"isActive":{"type":"boolean","description":"Enable/disable the scheduled message"}}}}}},"responses":{"200":{"description":"Scheduled message updated successfully"},"400":{"description":"Invalid request body"},"401":{"description":"Unauthorized"},"404":{"description":"Scheduled message not found"}}},"delete":{"tags":["Scheduled Messages"],"description":"Delete a scheduled message","x-required-roles":["kb:admin","kb:superadmin"],"parameters":[{"name":"id","in":"path","description":"Scheduled message ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Scheduled message deleted successfully"},"401":{"description":"Unauthorized"},"404":{"description":"Scheduled message not found"}}},"servers":[{"url":"/"}]},"/v1/api/scheduled-messages/{id}/toggle":{"post":{"tags":["Scheduled Messages"],"description":"Toggle the active state of a scheduled message","x-required-roles":["kb:admin","kb:superadmin"],"parameters":[{"name":"id","in":"path","description":"Scheduled message ID","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Scheduled message toggled successfully"},"401":{"description":"Unauthorized"},"404":{"description":"Scheduled message not found"}}},"servers":[{"url":"/"}]},"/docs":{"get":{"tags":["System"],"description":"Interactive API documentation (Scalar)","x-auth-bypass":true,"responses":{"200":{"description":"API documentation page"}}}},"/changelog":{"get":{"tags":["System"],"description":"Structured changelog for this service. Returns only entries that directly affect @botticelli/mastra-operators, grouped by release and change type.","requestBody":{"query":{"limit":{"type":"number","description":"Maximum number of releases to return (default: all)"}}},"responses":{"200":{"description":"Changelog as structured JSON","content":{"application/json":{"schema":{"type":"object","properties":{"package":{"type":"string"},"releases":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"version":{"type":"string"},"summary":{"type":"string"},"breaking":{"type":"array","items":{"type":"string"}},"new_capabilities":{"type":"array","items":{"type":"string"}},"changes":{"type":"array","items":{"type":"string"}},"fixes":{"type":"array","items":{"type":"string"}},"removed":{"type":"array","items":{"type":"string"}}}}}}}}}},"404":{"description":"Changelog has not been generated yet"}}}},"/llms.txt":{"get":{"tags":["System"],"description":"LLM-friendly API documentation in llms.txt format (https://llmstxt.org). Lists all public endpoints grouped by tag with parameters and response shapes.","x-auth-bypass":true,"responses":{"200":{"description":"llms.txt document"},"502":{"description":"Could not fetch OpenAPI spec"}}}},"/metrics":{"get":{"tags":["System"],"description":"Prometheus metrics exposition (retrieval SLI counters/histograms). Unauthenticated.","x-auth-bypass":true,"responses":{"200":{"description":"Prometheus text exposition format","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/api/agents/{agentId}/stream":{"post":{"tags":["Agents","Legacy"],"deprecated":true,"summary":"Stream agent response (Legacy AI SDK v1)","description":"Streaming endpoint using AI SDK v1 data-stream wire format. Requests are immediately returned as a stream and internally proxied via the early-stream service to /api/agents/:agentId/stream-legacy. To run the agent inside a data room and let it reach the knowledge bases connected to that data room, send the `x-blockbrain-active-bot-id` and `x-blockbrain-data-room-id` headers. Without them the call still succeeds, but the agent answers as if no knowledge base were connected. Migrate to POST /v2/api/agents/:agentId/stream (AI SDK v6 UIMessage format).","parameters":[{"name":"agentId","in":"path","required":true,"description":"Unique identifier for the agent","schema":{"type":"string"}},{"name":"x-tool-ids","in":"header","required":false,"description":"Comma-separated list of tool IDs to enable for this request. Used by non-Blocky clients (e.g., Eddy) that do not send x-blockbrain-data-room-id. When present, the agent is restricted to these tools. Omitting the header applies no filter, so the agent keeps whatever its capability defaults resolved. Listing a tool the agent does not already have does not grant it.","schema":{"type":"string"}},{"name":"x-blockbrain-active-bot-id","in":"header","required":false,"description":"Active bot id. `POST /cortex/active-bot/{botId}/convo` on blocky returns `{ dataRoomId, activeBotId }` in one response, so a single call yields both headers. That call creates a new data room, so use it only when starting a fresh conversation. That call accepts a predefined bot id and fetches or creates the active bot behind it, so read `activeBotId` from its response body rather than reusing the path id. For a data room that already exists, its active bot id is the room's `data[].botId` in `GET /cortex/active-bot/{activeBotId}/dataroom`, whose own path id is already an active bot id and not a predefined one. Send it together with `x-blockbrain-data-room-id` to run the agent inside a data room and reach the knowledge bases connected to it. Omit both only for non-Blocky clients that select their tools with `x-tool-ids`. If either header is missing the request still succeeds. The agent runs with a minimal context, has no data room, and cannot reach the knowledge bases connected to it, so it answers as if no knowledge base were connected. Nothing fails and no error is returned, which is what makes an omitted header hard to spot.","schema":{"type":"string"}},{"name":"x-blockbrain-data-room-id","in":"header","required":false,"description":"Conversation id, despite the header name. Blocky fills its `dataRoomId` response field with the conversation id, and this service resolves the header through `GET /cortex/conversation/{id}`. `POST /cortex/active-bot/{botId}/convo` on blocky returns `{ dataRoomId, activeBotId }` in one response, so a single call yields both headers. That call creates a new data room, so use it only when starting a fresh conversation. To stream into a data room that already exists, take `data[].convos[]._id` from `GET /cortex/active-bot/{activeBotId}/dataroom` (pinned rooms sit under `pinedDatarooms[]` on the same response). Do not send that response's `data[]._id`: that is the data room entity id on the same element, and this header does not resolve it. Send it together with `x-blockbrain-active-bot-id` to run the agent inside a data room and reach the knowledge bases connected to it. Omit both only for non-Blocky clients that select their tools with `x-tool-ids`. If either header is missing the request still succeeds. The agent runs with a minimal context, has no data room, and cannot reach the knowledge bases connected to it, so it answers as if no knowledge base were connected. Nothing fails and no error is returned, which is what makes an omitted header hard to spot.","schema":{"type":"string"}},{"name":"x-blockbrain-organization-id","in":"header","required":false,"description":"Accepted for backward compatibility and ignored. The organization is taken from the access token, and `x-zitadel-org-id` selects it when the token carries roles for more than one. Sending this header changes nothing about organization, data room, or knowledge base resolution.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"anyOf":[{"type":"array"},{"type":"string"}],"description":"Conversation messages in AI SDK v1 format."},"threadId":{"type":"string","description":"Thread ID for memory persistence. If omitted, messages are not saved."},"resourceId":{"type":"string","description":"Resource (user) ID associated with the thread. Falls back to threadId if not provided."},"runId":{"type":"string","description":"Optional run ID for tracing."},"instructions":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Additional system instructions to prepend."},"maxSteps":{"type":"number","description":"Maximum number of agentic steps."}},"required":["messages"],"additionalProperties":true}}}},"responses":{"200":{"description":"AI SDK v1 data-stream (text/plain). Prefixed with early status tokens (\"preparing_request\", \"streaming\") emitted by the early-stream proxy.","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Unauthorized - missing or invalid access token"},"404":{"description":"Agent not found"},"500":{"description":"Internal server error"}}}},"/api/agents/{agentId}/stream-legacy":{"post":{"tags":["Agents","Legacy"],"deprecated":true,"summary":"Stream agent response — legacy internal target","description":"Internal target of the early-stream proxy rewrite — do not call directly. Use POST /api/agents/:agentId/stream (which proxies here via the early-stream service) or migrate to POST /v2/api/agents/:agentId/stream.","parameters":[{"name":"agentId","in":"path","required":true,"description":"Unique identifier for the agent","schema":{"type":"string"}},{"name":"x-tool-ids","in":"header","required":false,"description":"Comma-separated list of tool IDs to enable for this request. Used by non-Blocky clients (e.g., Eddy) that do not send x-blockbrain-data-room-id. When present, the agent is restricted to these tools. Omitting the header applies no filter, so the agent keeps whatever its capability defaults resolved. Listing a tool the agent does not already have does not grant it.","schema":{"type":"string"}},{"name":"x-blockbrain-active-bot-id","in":"header","required":false,"description":"Active bot id. `POST /cortex/active-bot/{botId}/convo` on blocky returns `{ dataRoomId, activeBotId }` in one response, so a single call yields both headers. That call creates a new data room, so use it only when starting a fresh conversation. That call accepts a predefined bot id and fetches or creates the active bot behind it, so read `activeBotId` from its response body rather than reusing the path id. For a data room that already exists, its active bot id is the room's `data[].botId` in `GET /cortex/active-bot/{activeBotId}/dataroom`, whose own path id is already an active bot id and not a predefined one. Send it together with `x-blockbrain-data-room-id` to run the agent inside a data room and reach the knowledge bases connected to it. Omit both only for non-Blocky clients that select their tools with `x-tool-ids`. If either header is missing the request still succeeds. The agent runs with a minimal context, has no data room, and cannot reach the knowledge bases connected to it, so it answers as if no knowledge base were connected. Nothing fails and no error is returned, which is what makes an omitted header hard to spot.","schema":{"type":"string"}},{"name":"x-blockbrain-data-room-id","in":"header","required":false,"description":"Conversation id, despite the header name. Blocky fills its `dataRoomId` response field with the conversation id, and this service resolves the header through `GET /cortex/conversation/{id}`. `POST /cortex/active-bot/{botId}/convo` on blocky returns `{ dataRoomId, activeBotId }` in one response, so a single call yields both headers. That call creates a new data room, so use it only when starting a fresh conversation. To stream into a data room that already exists, take `data[].convos[]._id` from `GET /cortex/active-bot/{activeBotId}/dataroom` (pinned rooms sit under `pinedDatarooms[]` on the same response). Do not send that response's `data[]._id`: that is the data room entity id on the same element, and this header does not resolve it. Send it together with `x-blockbrain-active-bot-id` to run the agent inside a data room and reach the knowledge bases connected to it. Omit both only for non-Blocky clients that select their tools with `x-tool-ids`. If either header is missing the request still succeeds. The agent runs with a minimal context, has no data room, and cannot reach the knowledge bases connected to it, so it answers as if no knowledge base were connected. Nothing fails and no error is returned, which is what makes an omitted header hard to spot.","schema":{"type":"string"}},{"name":"x-blockbrain-organization-id","in":"header","required":false,"description":"Accepted for backward compatibility and ignored. The organization is taken from the access token, and `x-zitadel-org-id` selects it when the token carries roles for more than one. Sending this header changes nothing about organization, data room, or knowledge base resolution.","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"anyOf":[{"type":"array"},{"type":"string"}],"description":"Conversation messages in AI SDK v1 format."},"threadId":{"type":"string","description":"Thread ID for memory persistence. If omitted, messages are not saved."},"resourceId":{"type":"string","description":"Resource (user) ID associated with the thread. Falls back to threadId if not provided."},"runId":{"type":"string","description":"Optional run ID for tracing."},"instructions":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}],"description":"Additional system instructions to prepend."},"maxSteps":{"type":"number","description":"Maximum number of agentic steps."}},"required":["messages"],"additionalProperties":true}}}},"responses":{"200":{"description":"AI SDK v1 data-stream (text/plain). Prefixed with early status tokens (\"preparing_request\", \"streaming\") emitted by the early-stream proxy.","content":{"text/plain":{"schema":{"type":"string"}}}},"401":{"description":"Unauthorized - missing or invalid access token"},"404":{"description":"Agent not found"},"500":{"description":"Internal server error"}}}}},"servers":[{"url":"/api"}]}