The selected provider, model, reasoning, endpoint, and credential-presence flag. Secrets and vault labels are never returned.
API reference sections
HTTP API reference
AI chats
AI chats, turns and attempts, messages, tools and tool calls, providers, search, exports and usage.
48 operations. The contract has no example values, so example bodies are generated from each schema: every field is shown with a placeholder of its type.
GET/v1/ai/providers/settings
- Authorization
- Bearer token with
ai.manage_providers - Operation id
- ai_provider_settings
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ProviderSettingsResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"selected_provider": "string",
"openai": {
"model": "string",
"reasoning": "default",
"credential_configured": false
},
"ollama": {
"endpoint": "string",
"model": "string",
"reasoning": "default"
},
"agents": [
{
"id": "string",
"label": "string",
"steers": false
}
]
}curl "http://127.0.0.1:$PORT/v1/ai/providers/settings" \
-H "Authorization: Bearer $KORU_TOKEN"PUT/v1/ai/providers/selection
The complete persisted non-secret provider settings.
- Authorization
- Bearer token with
ai.manage_providers - Operation id
- ai_select_provider
Request body (required)
application/json SelectProviderRequest
| Field | Type | Description |
|---|---|---|
| providerrequired | AiProviderId | Extensible provider token such as openai, ollama, or koru. |
{
"provider": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ProviderSettingsResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"selected_provider": "string",
"openai": {
"model": "string",
"reasoning": "default",
"credential_configured": false
},
"ollama": {
"endpoint": "string",
"model": "string",
"reasoning": "default"
},
"agents": [
{
"id": "string",
"label": "string",
"steers": false
}
]
}curl -X PUT "http://127.0.0.1:$PORT/v1/ai/providers/selection" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"provider": "string"
}'PUT/v1/ai/providers/openai
The saved OpenAI model, reasoning choice, and credential-presence flag.
- Authorization
- Bearer token with
ai.manage_providers - Operation id
- ai_update_openai_settings
Request body (required)
application/json UpdateOpenAiSettingsRequest
| Field | Type | Description |
|---|---|---|
| model | string or null | |
| reasoningrequired | ReasoningChoice | A provider's reasoning setting - |
{
"model": "string",
"reasoning": "default"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | OpenAiSettingsView |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"model": "string",
"reasoning": "default",
"credential_configured": false
}curl -X PUT "http://127.0.0.1:$PORT/v1/ai/providers/openai" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "string",
"reasoning": "default"
}'PUT/v1/ai/providers/ollama
The saved Ollama endpoint, model, and reasoning choice.
- Authorization
- Bearer token with
ai.manage_providers - Operation id
- ai_update_ollama_settings
Request body (required)
application/json UpdateOllamaSettingsRequest
| Field | Type | Description |
|---|---|---|
| endpointrequired | string | |
| model | string or null | |
| reasoningrequired | ReasoningChoice | A provider's reasoning setting - |
{
"endpoint": "string",
"model": "string",
"reasoning": "default"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | OllamaSettingsView |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"endpoint": "string",
"model": "string",
"reasoning": "default"
}curl -X PUT "http://127.0.0.1:$PORT/v1/ai/providers/ollama" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"endpoint": "string",
"model": "string",
"reasoning": "default"
}'POST/v1/ai/providers/{provider}/validate
A safe provider state, available model names, and selected-model capabilities when ready.
- Authorization
- Bearer token with
ai.manage_providers - Operation id
- ai_validate_provider
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| providerrequired | path | string |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ProviderValidationResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"provider": "string",
"status": "unconfigured",
"models": [
"string"
],
"selected_model": {
"selection": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"resolved_model": "string",
"capabilities": {
"streaming": false,
"structured_output": false,
"vision": false,
"web_search": false,
"context_window": 0,
"reasoning": {
"kind": "unsupported"
}
}
},
"failure": {
"code": "string",
"message": "string",
"retryable": false,
"details": {}
}
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/providers/$PROVIDER/validate" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/ai/providers/{provider}/models
The agent's models in its own ids, each with the agent's grouping; empty when it lists none. With the optional body { "refresh": true } the list is rebuilt first.
- Authorization
- Bearer token with
ai.manage_providers - Operation id
- ai_discover_agent_models
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| providerrequired | path | string |
Request body (required)
application/json DiscoverAgentModelsRequest
| Field | Type | Description |
|---|---|---|
| refresh | boolean | Default |
{
"refresh": false
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | AgentModelsResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"provider": "string",
"models": [
{
"id": "string",
"group": "string",
"name": "string",
"description": "string",
"group_name": "string",
"legacy": false,
"reasoning": {
"kind": "unsupported"
},
"context": {
"id": "string",
"tokens": 0
},
"fast": {
"on_by_default": false
}
}
],
"modes": [
{
"id": "string",
"name": "string",
"description": "string"
}
],
"default_mode": "string",
"levels_per_turn": false,
"models_per_turn": false
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/providers/$PROVIDER/models" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"refresh": false
}'POST/v1/ai/providers/{provider}/commands
The agent's skills for the folder - Codex's skills/list, enabled ones only - in a picker's order, each a name, one line and the agent's display name; empty when it offers none. The body names the folder; absent, the home folder.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_discover_agent_commands
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| providerrequired | path | string |
Request body (required)
application/json DiscoverAgentCommandsRequest
| Field | Type | Description |
|---|---|---|
| folder | string or null |
{
"folder": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | AgentCommandsResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"provider": "string",
"commands": [
{
"name": "string",
"description": "string",
"display_name": "string",
"kind": "command",
"input_hint": "string"
}
]
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/providers/$PROVIDER/commands" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"folder": "string"
}'POST/v1/ai/providers/ollama/pulls
An SSE stream beginning with the accepted model followed by normalized progress and one terminal event.
- Authorization
- Bearer token with
ai.manage_providers - Operation id
- ai_pull_ollama_model
Request body (required)
application/json PullOllamaModelRequest
| Field | Type | Description |
|---|---|---|
| modelrequired | string |
{
"model": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | PullOllamaModelAccepted or ModelPullEventtext/event-stream |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
curl -X POST "http://127.0.0.1:$PORT/v1/ai/providers/ollama/pulls" \
-N \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "string"
}'POST/v1/ai/providers/ollama/pulls/cancel
Whether an active pull was signalled for cancellation.
- Authorization
- Bearer token with
ai.manage_providers - Operation id
- ai_cancel_ollama_pull
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | CancelModelPullResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"cancelled": false
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/providers/ollama/pulls/cancel" \
-H "Authorization: Bearer $KORU_TOKEN"GET/v1/ai/chats
Durable chats ordered by most recent activity: one profile's, given profile, else every profile's.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_list_chats
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| profile | query | string (uuid) | Only this profile's chats (AI schema 13). Absent lists every profile's. |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ListChatsResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chats": [
{
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"private": false,
"current_model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"branched_from": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000"
},
"agent_working_folder": "string",
"agent_mode": "string",
"agent_fast": false,
"pinned_at": 0,
"profile_id": "00000000-0000-4000-8000-000000000000",
"created_at": 0,
"updated_at": 0
}
]
}curl "http://127.0.0.1:$PORT/v1/ai/chats" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/ai/chats
The durable chat created with the client-minted identity.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_create_chat
Request body (required)
application/json CreateChatRequest
| Field | Type | Description |
|---|---|---|
| chat_idrequired | ChatId | ChatId - a client-minted UUID v4, never derived from content or location. |
| titlerequired | string | |
| modelrequired | ModelSelection | |
| profile_id | ProfileId or null | The profile the chat belongs to (AI schema 13): the profile of the window it is started in. An older client sends none, and the service places the chat as it places one written before profiles - with nothing read yet, in the first profile. |
{
"chat_id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"profile_id": "00000000-0000-4000-8000-000000000000"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | CreateChatResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chat": {
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"private": false,
"current_model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"branched_from": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000"
},
"agent_working_folder": "string",
"agent_mode": "string",
"agent_fast": false,
"pinned_at": 0,
"profile_id": "00000000-0000-4000-8000-000000000000",
"created_at": 0,
"updated_at": 0
}
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/chats" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chat_id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"profile_id": "00000000-0000-4000-8000-000000000000"
}'DELETE/v1/ai/chats
The number of chats deleted. Anonymous text-free usage and feedback aggregates remain until cleared separately.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_clear_history
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ClearHistoryResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chats_deleted": 0
}curl -X DELETE "http://127.0.0.1:$PORT/v1/ai/chats" \
-H "Authorization: Bearer $KORU_TOKEN"GET/v1/ai/chats/{chat}
The chat, ordered messages, finalized blocks, and pinned document references.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_read_chat
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ChatTranscript |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chat": {
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"private": false,
"current_model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"branched_from": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000"
},
"agent_working_folder": "string",
"agent_mode": "string",
"agent_fast": false,
"pinned_at": 0,
"profile_id": "00000000-0000-4000-8000-000000000000",
"created_at": 0,
"updated_at": 0
},
"tool_policy": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"mode": "manual",
"workspace_ids": [
"00000000-0000-4000-8000-000000000000"
]
},
"tool_activities": [],
"notices": [
{
"id": "00000000-0000-4000-8000-000000000000",
"attempt_id": "00000000-0000-4000-8000-000000000000",
"chat_id": "00000000-0000-4000-8000-000000000000",
"assistant_message_id": "00000000-0000-4000-8000-000000000000",
"text": "string",
"text_offset": 0,
"created_at": 0
}
],
"messages": [
{
"id": "00000000-0000-4000-8000-000000000000",
"chat_id": "00000000-0000-4000-8000-000000000000",
"ordinal": 0,
"role": "user",
"state": "pending",
"markdown": "string",
"origin_message_id": "00000000-0000-4000-8000-000000000000",
"provider": "string",
"requested_model": "string",
"resolved_model": "string",
"error_class": "configuration",
"created_at": 0,
"completed_at": 0
}
],
"blocks": [
{
"id": "00000000-0000-4000-8000-000000000000",
"message_id": "00000000-0000-4000-8000-000000000000",
"ordinal": 0,
"kind": "paragraph",
"markdown": "string",
"plain_text": "string"
}
],
"pinned_documents": [
{
"chat_id": "00000000-0000-4000-8000-000000000000",
"document_id": "00000000-0000-4000-8000-000000000000",
"display_title": "string",
"pinned": false,
"first_used_at": 0,
"last_used_at": 0
}
],
"feedback": [],
"usage": [],
"sources": [],
"reasoning_control": {
"kind": "unsupported"
},
"agent_commands": [
{
"name": "string",
"description": "string",
"display_name": "string",
"kind": "command",
"input_hint": "string"
}
],
"attachments": []
}curl "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT" \
-H "Authorization: Bearer $KORU_TOKEN"PATCH/v1/ai/chats/{chat}
The durable chat with its updated title and activity timestamp.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_rename_chat
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json RenameChatRequest
| Field | Type | Description |
|---|---|---|
| titlerequired | string |
{
"title": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | RenameChatResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chat": {
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"private": false,
"current_model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"branched_from": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000"
},
"agent_working_folder": "string",
"agent_mode": "string",
"agent_fast": false,
"pinned_at": 0,
"profile_id": "00000000-0000-4000-8000-000000000000",
"created_at": 0,
"updated_at": 0
}
}curl -X PATCH "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "string"
}'DELETE/v1/ai/chats/{chat}
Whether the chat existed. Content, search rows, and chat/message identifiers are removed while text-free attempt and feedback metrics survive.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_delete_chat
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | DeleteChatResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"deleted": false
}curl -X DELETE "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT" \
-H "Authorization: Bearer $KORU_TOKEN"PATCH/v1/ai/chats/{chat}/pin
The durable chat with its pin time, or none once unpinned; its activity timestamp is unchanged.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_pin_chat
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json PinChatRequest
| Field | Type | Description |
|---|---|---|
| pinnedrequired | boolean |
{
"pinned": false
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | PinChatResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chat": {
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"private": false,
"current_model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"branched_from": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000"
},
"agent_working_folder": "string",
"agent_mode": "string",
"agent_fast": false,
"pinned_at": 0,
"profile_id": "00000000-0000-4000-8000-000000000000",
"created_at": 0,
"updated_at": 0
}
}curl -X PATCH "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/pin" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"pinned": false
}'PATCH/v1/ai/chats/{chat}/model
The durable chat with its updated provider, model, reasoning default, and activity timestamp.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_update_chat_model
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json UpdateChatModelRequest
| Field | Type | Description |
|---|---|---|
| modelrequired | ModelSelection |
{
"model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
}
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | UpdateChatModelResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chat": {
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"private": false,
"current_model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"branched_from": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000"
},
"agent_working_folder": "string",
"agent_mode": "string",
"agent_fast": false,
"pinned_at": 0,
"profile_id": "00000000-0000-4000-8000-000000000000",
"created_at": 0,
"updated_at": 0
},
"reasoning_control": {
"kind": "unsupported"
}
}curl -X PATCH "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/model" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
}
}'PATCH/v1/ai/chats/{chat}/privacy
The durable chat with its updated privacy flag and activity timestamp.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_update_chat_privacy
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json UpdateChatPrivacyRequest
| Field | Type | Description |
|---|---|---|
| privaterequired | boolean |
{
"private": false
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | UpdateChatPrivacyResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chat": {
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"private": false,
"current_model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"branched_from": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000"
},
"agent_working_folder": "string",
"agent_mode": "string",
"agent_fast": false,
"pinned_at": 0,
"profile_id": "00000000-0000-4000-8000-000000000000",
"created_at": 0,
"updated_at": 0
}
}curl -X PATCH "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/privacy" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"private": false
}'PATCH/v1/ai/chats/{chat}/folder
The durable chat with its working folder; accepted only on an agent chat with no message yet.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_update_chat_folder
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json UpdateChatFolderRequest
| Field | Type | Description |
|---|---|---|
| folder | string or null |
{
"folder": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | UpdateChatFolderResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chat": {
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"private": false,
"current_model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"branched_from": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000"
},
"agent_working_folder": "string",
"agent_mode": "string",
"agent_fast": false,
"pinned_at": 0,
"profile_id": "00000000-0000-4000-8000-000000000000",
"created_at": 0,
"updated_at": 0
}
}curl -X PATCH "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/folder" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"folder": "string"
}'PATCH/v1/ai/chats/{chat}/agent-settings
The durable chat with its agent settings; accepted on an agent chat at any time, a running reply keeping the mode it was sent in.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_update_chat_agent_settings
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json UpdateChatAgentSettingsRequest
| Field | Type | Description |
|---|---|---|
| mode | string or null | The approval mode, the agent's own id: one it lists in [ |
| fast | boolean or null | Fast mode on or off for the chat's model (koru-ai D11), where the model offers it ([ |
{
"mode": "string",
"fast": false
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | UpdateChatAgentSettingsResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chat": {
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"private": false,
"current_model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"branched_from": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000"
},
"agent_working_folder": "string",
"agent_mode": "string",
"agent_fast": false,
"pinned_at": 0,
"profile_id": "00000000-0000-4000-8000-000000000000",
"created_at": 0,
"updated_at": 0
}
}curl -X PATCH "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/agent-settings" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mode": "string",
"fast": false
}'POST/v1/ai/tools/chats/list
A bounded newest-first page of non-private chat summaries.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_tool_list_chats
Request body (required)
application/json ListChatsToolRequest
| Field | Type | Description |
|---|---|---|
| cursor | string or null | Default |
| page_size | integer (uint16, >= 0, <= 65535) or null | Default |
{
"cursor": "string",
"page_size": 0
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ListChatsToolResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chats": [
{
"chat_id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"message_count": 0,
"created_at": 0,
"updated_at": 0
}
],
"next_cursor": "string"
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/tools/chats/list" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cursor": "string",
"page_size": 0
}'POST/v1/ai/tools/chats/search
A bounded relevance-ranked page of finalized response matches from non-private chats.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_tool_search_chats
Request body (required)
application/json SearchChatsToolRequest
| Field | Type | Description |
|---|---|---|
| queryrequired | string | |
| cursor | string or null | Default |
| page_size | integer (uint16, >= 0, <= 65535) or null | Default |
{
"query": "string",
"cursor": "string",
"page_size": 0
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | SearchChatsToolResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"hits": [
{
"chat_id": "00000000-0000-4000-8000-000000000000",
"message_id": "00000000-0000-4000-8000-000000000000",
"chat_title": "string",
"snippet": "string",
"score": 0
}
],
"next_cursor": "string"
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/tools/chats/search" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"query": "string",
"cursor": "string",
"page_size": 0
}'POST/v1/ai/tools/chats/read
A byte-bounded recent page of terminal chat messages; private and missing chats share chat_not_found.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_tool_read_chat
Request body (required)
application/json ReadChatToolRequest
| Field | Type | Description |
|---|---|---|
| chat_idrequired | ChatId | ChatId - a client-minted UUID v4, never derived from content or location. |
| cursor | string or null | Default |
| page_size | integer (uint16, >= 0, <= 65535) or null | Default |
{
"chat_id": "00000000-0000-4000-8000-000000000000",
"cursor": "string",
"page_size": 0
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ReadChatToolResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"chat_id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"total_messages": 0,
"messages": [
{
"message_id": "00000000-0000-4000-8000-000000000000",
"ordinal": 0,
"role": "user",
"state": "pending",
"markdown": "string",
"truncated": false,
"created_at": 0
}
],
"next_cursor": "string"
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/tools/chats/read" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chat_id": "00000000-0000-4000-8000-000000000000",
"cursor": "string",
"page_size": 0
}'GET/v1/ai/chats/{chat}/tool-policy
The durable Manual or Auto mode and complete selected-workspace grant set.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_read_chat_tool_policy
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ChatToolPolicyResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"policy": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"mode": "manual",
"workspace_ids": [
"00000000-0000-4000-8000-000000000000"
]
}
}curl "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/tool-policy" \
-H "Authorization: Bearer $KORU_TOKEN"PUT/v1/ai/chats/{chat}/tool-policy
The durable Manual or Auto mode and complete selected-workspace grant set. An empty set grants no workspace access.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_update_chat_tool_policy
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json UpdateChatToolPolicyRequest
| Field | Type | Description |
|---|---|---|
| moderequired | ToolMode | |
| workspace_idsrequired | array of WorkspaceId |
{
"mode": "manual",
"workspace_ids": [
"00000000-0000-4000-8000-000000000000"
]
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ChatToolPolicyResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"policy": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"mode": "manual",
"workspace_ids": [
"00000000-0000-4000-8000-000000000000"
]
}
}curl -X PUT "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/tool-policy" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mode": "manual",
"workspace_ids": [
"00000000-0000-4000-8000-000000000000"
]
}'POST/v1/ai/chats/{chat}/tool-policy/grants
The durable policy with all existing and newly granted workspaces; the mode is unchanged.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_add_chat_workspace_grants
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json AddChatWorkspaceGrantsRequest
| Field | Type | Description |
|---|---|---|
| workspace_idsrequired | array of WorkspaceId |
{
"workspace_ids": [
"00000000-0000-4000-8000-000000000000"
]
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ChatToolPolicyResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"policy": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"mode": "manual",
"workspace_ids": [
"00000000-0000-4000-8000-000000000000"
]
}
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/tool-policy/grants" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"workspace_ids": [
"00000000-0000-4000-8000-000000000000"
]
}'GET/v1/ai/usage
Content-free local aggregates grouped by day, surface, provider, model, effort, status, and prompt version. Missing provider usage remains unavailable.
- Authorization
- Bearer token with
ai.read_usage - Operation id
- ai_usage
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | UsageResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"report": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"current_chat": {
"attempts": 0,
"attempts_with_reported_usage": 0,
"request_payload_bytes": 0,
"web_search_calls": 0,
"context_payload_bytes": 0,
"message_count": 0,
"context_document_count": 0,
"context_message_block_count": 0,
"prompt_tokens": 0,
"cached_prompt_tokens": 0,
"completion_tokens": 0,
"reasoning_tokens": 0,
"total_tokens": 0,
"average_context_assembly_ms": 0,
"average_provider_start_ms": 0,
"average_time_to_first_token_ms": 0,
"average_generation_ms": 0,
"average_total_ms": 0,
"good_feedback": 0,
"bad_feedback": 0
},
"groups": [
{
"utc_day": "string",
"surface": "standalone",
"provider": "string",
"model": "string",
"reasoning": "default",
"status": "pending",
"prompt_template_version": "string",
"totals": {
"attempts": 0,
"attempts_with_reported_usage": 0,
"request_payload_bytes": 0,
"web_search_calls": 0,
"context_payload_bytes": 0,
"message_count": 0,
"context_document_count": 0,
"context_message_block_count": 0,
"prompt_tokens": 0,
"cached_prompt_tokens": 0,
"completion_tokens": 0,
"reasoning_tokens": 0,
"total_tokens": 0,
"average_context_assembly_ms": 0,
"average_provider_start_ms": 0,
"average_time_to_first_token_ms": 0,
"average_generation_ms": 0,
"average_total_ms": 0,
"good_feedback": 0,
"bad_feedback": 0
}
}
]
}
}curl "http://127.0.0.1:$PORT/v1/ai/usage" \
-H "Authorization: Bearer $KORU_TOKEN"DELETE/v1/ai/usage
The number of text-free attempts and feedback events deleted. Chat history is unchanged.
- Authorization
- Bearer token with
ai.read_usage - Operation id
- ai_clear_usage
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ClearUsageResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"attempts_deleted": 0,
"feedback_events_deleted": 0
}curl -X DELETE "http://127.0.0.1:$PORT/v1/ai/usage" \
-H "Authorization: Bearer $KORU_TOKEN"GET/v1/ai/chats/{chat}/usage
Current-chat totals followed by the same content-free all-usage aggregates as the local usage report.
- Authorization
- Bearer token with
ai.read_usage - Operation id
- ai_chat_usage
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | UsageResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"report": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"current_chat": {
"attempts": 0,
"attempts_with_reported_usage": 0,
"request_payload_bytes": 0,
"web_search_calls": 0,
"context_payload_bytes": 0,
"message_count": 0,
"context_document_count": 0,
"context_message_block_count": 0,
"prompt_tokens": 0,
"cached_prompt_tokens": 0,
"completion_tokens": 0,
"reasoning_tokens": 0,
"total_tokens": 0,
"average_context_assembly_ms": 0,
"average_provider_start_ms": 0,
"average_time_to_first_token_ms": 0,
"average_generation_ms": 0,
"average_total_ms": 0,
"good_feedback": 0,
"bad_feedback": 0
},
"groups": [
{
"utc_day": "string",
"surface": "standalone",
"provider": "string",
"model": "string",
"reasoning": "default",
"status": "pending",
"prompt_template_version": "string",
"totals": {
"attempts": 0,
"attempts_with_reported_usage": 0,
"request_payload_bytes": 0,
"web_search_calls": 0,
"context_payload_bytes": 0,
"message_count": 0,
"context_document_count": 0,
"context_message_block_count": 0,
"prompt_tokens": 0,
"cached_prompt_tokens": 0,
"completion_tokens": 0,
"reasoning_tokens": 0,
"total_tokens": 0,
"average_context_assembly_ms": 0,
"average_provider_start_ms": 0,
"average_time_to_first_token_ms": 0,
"average_generation_ms": 0,
"average_total_ms": 0,
"good_feedback": 0,
"bad_feedback": 0
}
}
]
}
}curl "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/usage" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/ai/search
Best matching finalized block per response with exact chat, message, and block identity.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_search
Request body (required)
application/json AiSearchRequest
| Field | Type | Description |
|---|---|---|
| textrequired | string | |
| limit | integer (uint16, >= 0, <= 65535) | Default |
| cursor | string or null | |
| request_seq | integer (uint64, >= 0) | Default |
| profile | ProfileId or null | Only this profile's chats (AI schema 13). Absent searches every profile's; each hit names its own. |
{
"text": "string",
"limit": 50,
"cursor": "string",
"request_seq": 0,
"profile": "00000000-0000-4000-8000-000000000000"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | AiSearchResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"page": {
"hits": [
{
"chat_id": "00000000-0000-4000-8000-000000000000",
"message_id": "00000000-0000-4000-8000-000000000000",
"block_id": "00000000-0000-4000-8000-000000000000",
"chat_title": "string",
"snippet": "string",
"spans": [
{
"start": 0,
"end": 0
}
],
"score": 0,
"profile_id": "00000000-0000-4000-8000-000000000000"
}
],
"next_cursor": "string"
},
"request_seq": 0
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/search" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "string",
"limit": 50,
"cursor": "string",
"request_seq": 0,
"profile": "00000000-0000-4000-8000-000000000000"
}'PUT/v1/ai/messages/{message}/feedback
The retry-safe append-only feedback event.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_rate_response
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| messagerequired | path | string (uuid) |
Request body (required)
application/json FeedbackRequest
| Field | Type | Description |
|---|---|---|
| event_idrequired | FeedbackEventId | FeedbackEventId - a client-minted UUID v4, never derived from content or location. |
| valuerequired | FeedbackValue | |
| surfacerequired | AiSurface |
{
"event_id": "00000000-0000-4000-8000-000000000000",
"value": "good",
"surface": "standalone"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | FeedbackResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"event": {
"id": "00000000-0000-4000-8000-000000000000",
"chat_id": "00000000-0000-4000-8000-000000000000",
"message_id": "00000000-0000-4000-8000-000000000000",
"attempt_id": "00000000-0000-4000-8000-000000000000",
"value": "good",
"surface": "standalone",
"created_at": 0
}
}curl -X PUT "http://127.0.0.1:$PORT/v1/ai/messages/$MESSAGE/feedback" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event_id": "00000000-0000-4000-8000-000000000000",
"value": "good",
"surface": "standalone"
}'POST/v1/ai/chats/{chat}/branches
A durable chat containing newly identified copies through the chosen response.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_branch_chat
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json BranchChatRequest
{
"branch_chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000",
"title": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | BranchChatResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"transcript": {
"chat": {
"id": "00000000-0000-4000-8000-000000000000",
"title": "string",
"private": false,
"current_model": {
"provider": "string",
"requested_model": "string",
"default_reasoning": "default"
},
"branched_from": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000"
},
"agent_working_folder": "string",
"agent_mode": "string",
"agent_fast": false,
"pinned_at": 0,
"profile_id": "00000000-0000-4000-8000-000000000000",
"created_at": 0,
"updated_at": 0
},
"tool_policy": {
"chat_id": "00000000-0000-4000-8000-000000000000",
"mode": "manual",
"workspace_ids": [
"00000000-0000-4000-8000-000000000000"
]
},
"tool_activities": [],
"notices": [
{
"id": "00000000-0000-4000-8000-000000000000",
"attempt_id": "00000000-0000-4000-8000-000000000000",
"chat_id": "00000000-0000-4000-8000-000000000000",
"assistant_message_id": "00000000-0000-4000-8000-000000000000",
"text": "string",
"text_offset": 0,
"created_at": 0
}
],
"messages": [
{
"id": "00000000-0000-4000-8000-000000000000",
"chat_id": "00000000-0000-4000-8000-000000000000",
"ordinal": 0,
"role": "user",
"state": "pending",
"markdown": "string",
"origin_message_id": "00000000-0000-4000-8000-000000000000",
"provider": "string",
"requested_model": "string",
"resolved_model": "string",
"error_class": "configuration",
"created_at": 0,
"completed_at": 0
}
],
"blocks": [
{
"id": "00000000-0000-4000-8000-000000000000",
"message_id": "00000000-0000-4000-8000-000000000000",
"ordinal": 0,
"kind": "paragraph",
"markdown": "string",
"plain_text": "string"
}
],
"pinned_documents": [
{
"chat_id": "00000000-0000-4000-8000-000000000000",
"document_id": "00000000-0000-4000-8000-000000000000",
"display_title": "string",
"pinned": false,
"first_used_at": 0,
"last_used_at": 0
}
],
"feedback": [],
"usage": [],
"sources": [],
"reasoning_control": {
"kind": "unsupported"
},
"agent_commands": [
{
"name": "string",
"description": "string",
"display_name": "string",
"kind": "command",
"input_hint": "string"
}
],
"attachments": []
}
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/branches" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"branch_chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000",
"title": "string"
}'POST/v1/ai/chats/{chat}/retries
A durable fresh assistant entry and attempt, streamed without replacing failed history.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_retry_turn
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json RetryTurnRequest
| Field | Type | Description |
|---|---|---|
| operation_idrequired | string | |
| message_idrequired | MessageId | MessageId - a client-minted UUID v4, never derived from content or location. |
| surfacerequired | AiSurface | |
| explicit_refs | array of ContextRef | Default |
| reasoning_override | ReasoningChoice or null | |
| web_search | boolean | |
| replace_pinned_documents | boolean | When true, existing pinned documents are ignored and only the explicit refs are used as context for this turn. Used by the contextual AI pane, where the current document should replace prior context. |
{
"operation_id": "string",
"message_id": "00000000-0000-4000-8000-000000000000",
"surface": "standalone",
"explicit_refs": [],
"reasoning_override": "default",
"web_search": false,
"replace_pinned_documents": false
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | SendTurnAccepted or StreamEventtext/event-stream |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
curl -X POST "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/retries" \
-N \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"operation_id": "string",
"message_id": "00000000-0000-4000-8000-000000000000",
"surface": "standalone",
"explicit_refs": [],
"reasoning_override": "default",
"web_search": false,
"replace_pinned_documents": false
}'POST/v1/ai/chats/{chat}/exports
The durable retry-safe export operation; poll it until terminal.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_export_chat
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json ExportChatRequest
| Field | Type | Description |
|---|---|---|
| export_idrequired | DocumentExportId | DocumentExportId - a client-minted UUID v4, never derived from content or location. |
| idempotency_keyrequired | string | |
| through_message_idrequired | MessageId | MessageId - a client-minted UUID v4, never derived from content or location. |
| destinationrequired | DocumentDestination |
{
"export_id": "00000000-0000-4000-8000-000000000000",
"idempotency_key": "string",
"through_message_id": "00000000-0000-4000-8000-000000000000",
"destination": {
"profile_id": "00000000-0000-4000-8000-000000000000",
"workspace_id": "00000000-0000-4000-8000-000000000000",
"parent_document_id": "00000000-0000-4000-8000-000000000000",
"index": 0,
"title": "string"
}
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ExportChatResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"export": {
"id": "00000000-0000-4000-8000-000000000000",
"idempotency_key": "string",
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000",
"destination": {
"profile_id": "00000000-0000-4000-8000-000000000000",
"workspace_id": "00000000-0000-4000-8000-000000000000",
"parent_document_id": "00000000-0000-4000-8000-000000000000",
"index": 0,
"title": "string"
},
"status": "pending",
"document_id": "00000000-0000-4000-8000-000000000000",
"error_class": "configuration",
"created_at": 0,
"completed_at": 0
}
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/exports" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"export_id": "00000000-0000-4000-8000-000000000000",
"idempotency_key": "string",
"through_message_id": "00000000-0000-4000-8000-000000000000",
"destination": {
"profile_id": "00000000-0000-4000-8000-000000000000",
"workspace_id": "00000000-0000-4000-8000-000000000000",
"parent_document_id": "00000000-0000-4000-8000-000000000000",
"index": 0,
"title": "string"
}
}'GET/v1/ai/exports/{export}
The authoritative pending, complete, or failed export operation.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_read_export
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| exportrequired | path | string (uuid) |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ExportChatResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"export": {
"id": "00000000-0000-4000-8000-000000000000",
"idempotency_key": "string",
"chat_id": "00000000-0000-4000-8000-000000000000",
"through_message_id": "00000000-0000-4000-8000-000000000000",
"destination": {
"profile_id": "00000000-0000-4000-8000-000000000000",
"workspace_id": "00000000-0000-4000-8000-000000000000",
"parent_document_id": "00000000-0000-4000-8000-000000000000",
"index": 0,
"title": "string"
},
"status": "pending",
"document_id": "00000000-0000-4000-8000-000000000000",
"error_class": "configuration",
"created_at": 0,
"completed_at": 0
}
}curl "http://127.0.0.1:$PORT/v1/ai/exports/$EXPORT" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/ai/chats/{chat}/turns
An ordered SSE stream beginning with durable acceptance, followed by normalized Koru AI events. Recover a lost stream by reading the attempt and chat.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_send_turn
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json SendTurnRequest
| Field | Type | Description |
|---|---|---|
| operation_idrequired | string | |
| chat_idrequired | ChatId | ChatId - a client-minted UUID v4, never derived from content or location. |
| surfacerequired | AiSurface | |
| markdownrequired | string | |
| explicit_refs | array of ContextRef | Default |
| reasoning_override | ReasoningChoice or null | |
| web_search | boolean | |
| system_prompt_override | string or null | Debug-only, per-request replacement for Koru's bundled system prompt. Empty or omitted keeps the versioned bundled prompt. |
| replace_pinned_documents | boolean | When true, existing pinned documents are ignored and only the explicit refs are used as context for this turn. Used by the contextual AI pane, where the current document should replace prior context. |
| attachment_ids | array of ChatAttachmentId | Attachments already stored for this chat and not yet sent, in the order they go with the message. Only an agent chat takes any. |
| answers | NextMessageAnswers or null | A local agent's questions answered by this message (koru-ai D108): a set the agent asked in its reply and moved on from, whose answers go to it as the person's next message. The message is the answers alone - |
| skills | array of string | The skills the person picked (koru-ai D121), by the name a local agent lists each under ( |
{
"operation_id": "string",
"chat_id": "00000000-0000-4000-8000-000000000000",
"surface": "standalone",
"markdown": "string",
"explicit_refs": [],
"reasoning_override": "default",
"web_search": false,
"system_prompt_override": "string",
"replace_pinned_documents": false,
"attachment_ids": [
"00000000-0000-4000-8000-000000000000"
],
"answers": {
"invocation": {},
"answers": [
{
"question_id": "string",
"value": {
"label": "string",
"kind": "option"
}
}
]
},
"skills": [
"string"
]
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | SendTurnAccepted or StreamEventtext/event-stream |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
curl -X POST "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/turns" \
-N \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"operation_id": "string",
"chat_id": "00000000-0000-4000-8000-000000000000",
"surface": "standalone",
"markdown": "string",
"explicit_refs": [],
"reasoning_override": "default",
"web_search": false,
"system_prompt_override": "string",
"replace_pinned_documents": false,
"attachment_ids": [
"00000000-0000-4000-8000-000000000000"
],
"answers": {
"invocation": {},
"answers": [
{
"question_id": "string",
"value": {
"label": "string",
"kind": "option"
}
}
]
},
"skills": [
"string"
]
}'GET/v1/ai/attempts/{attempt}
The durable attempt state used to recover after stream loss.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_read_attempt
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| attemptrequired | path | string (uuid) |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | AttemptResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"attempt": {
"id": "00000000-0000-4000-8000-000000000000",
"operation_id": "string",
"chat_id": "00000000-0000-4000-8000-000000000000",
"assistant_message_id": "00000000-0000-4000-8000-000000000000",
"surface": "standalone",
"intent": "chat",
"provider": "string",
"requested_model": "string",
"resolved_model": "string",
"requested_reasoning": "default",
"effective_reasoning": "default",
"prompt_template_version": "string",
"status": "pending",
"queued_at": 0,
"context_started_at": 0,
"context_completed_at": 0,
"provider_started_at": 0,
"first_token_at": 0,
"completed_at": 0,
"request_payload_bytes": 0,
"web_search_requested": false,
"web_search_calls": 0,
"context_payload_bytes": 0,
"message_count": 0,
"context_document_count": 0,
"context_message_block_count": 0,
"usage": {
"prompt_tokens": 0,
"cached_prompt_tokens": 0,
"completion_tokens": 0,
"reasoning_tokens": 0,
"total_tokens": 0,
"source": "provider_reported"
},
"context_used_tokens": 0,
"context_limit_tokens": 0,
"stop_reason": "string",
"error_class": "configuration",
"provider_response_id": "string"
}
}curl "http://127.0.0.1:$PORT/v1/ai/attempts/$ATTEMPT" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/ai/attempts/{attempt}/cancel
The current terminal state; cancelling an already-terminal attempt is a successful no-op.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_cancel_attempt
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| attemptrequired | path | string (uuid) |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | CancelAttemptResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"attempt_id": "00000000-0000-4000-8000-000000000000",
"status": "pending"
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/attempts/$ATTEMPT/cancel" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/ai/attempts/{attempt}/steer
The person's message as recorded after the running reply, once the local agent took it. A reply that cannot take text now (ended, not an agent's, or an agent with no mid-turn input) answers 409 steer_unavailable; the client then sends the message after the reply. Repeating an operation_id is an idempotent read of the first answer.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_steer_attempt
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| attemptrequired | path | string (uuid) |
Request body (required)
application/json SteerAttemptRequest
| Field | Type | Description |
|---|---|---|
| operation_idrequired | string (uuid) | |
| markdownrequired | string |
{
"operation_id": "00000000-0000-4000-8000-000000000000",
"markdown": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | SteerAttemptResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"attempt_id": "00000000-0000-4000-8000-000000000000",
"user_message_id": "00000000-0000-4000-8000-000000000000",
"replayed": false
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/attempts/$ATTEMPT/steer" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"operation_id": "00000000-0000-4000-8000-000000000000",
"markdown": "string"
}'POST/v1/ai/tool-calls/{tool}/approve
The durable content-free tool activity. Repeating the same approval is an idempotent read of that state.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_approve_tool_call
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| toolrequired | path | string (uuid) |
Request body (required)
application/json ToolDecisionRequest
| Field | Type | Description |
|---|---|---|
| choice | string or null | |
| answers | array of AgentAnswer or null |
{
"choice": "string",
"answers": [
{
"question_id": "string",
"value": {
"label": "string",
"kind": "option"
}
}
]
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ToolDecisionResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"activity": {
"id": "00000000-0000-4000-8000-000000000000",
"attempt_id": "00000000-0000-4000-8000-000000000000",
"chat_id": "00000000-0000-4000-8000-000000000000",
"assistant_message_id": "00000000-0000-4000-8000-000000000000",
"tool_name": "string",
"read_only": false,
"status": "proposed",
"approval": "not_required",
"proposed_at": 0,
"started_at": 0,
"completed_at": 0,
"result_bytes": 0,
"error_code": "permission_denied",
"target_workspace_id": "00000000-0000-4000-8000-000000000000",
"target_document_id": "00000000-0000-4000-8000-000000000000",
"error_message": "string",
"agent_label": "string",
"agent_summary": "string",
"agent_input": "string",
"agent_output": "string",
"text_offset": 0,
"live": {
"choices": [
{
"id": "string",
"intent": "allow_once",
"label": "string"
}
],
"questions": [
{
"id": "string",
"header": "string",
"question": "string",
"options": [
{
"label": "string",
"description": "string"
}
],
"allow_other": false,
"secret": false
}
]
}
}
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/tool-calls/$TOOL/approve" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"choice": "string",
"answers": [
{
"question_id": "string",
"value": {
"label": "string",
"kind": "option"
}
}
]
}'POST/v1/ai/tool-calls/{tool}/deny
The durable denied activity. The provider receives a structured permission-denied result and may continue.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_deny_tool_call
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| toolrequired | path | string (uuid) |
Request body (required)
application/json ToolDecisionRequest
| Field | Type | Description |
|---|---|---|
| choice | string or null | |
| answers | array of AgentAnswer or null |
{
"choice": "string",
"answers": [
{
"question_id": "string",
"value": {
"label": "string",
"kind": "option"
}
}
]
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ToolDecisionResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
{
"activity": {
"id": "00000000-0000-4000-8000-000000000000",
"attempt_id": "00000000-0000-4000-8000-000000000000",
"chat_id": "00000000-0000-4000-8000-000000000000",
"assistant_message_id": "00000000-0000-4000-8000-000000000000",
"tool_name": "string",
"read_only": false,
"status": "proposed",
"approval": "not_required",
"proposed_at": 0,
"started_at": 0,
"completed_at": 0,
"result_bytes": 0,
"error_code": "permission_denied",
"target_workspace_id": "00000000-0000-4000-8000-000000000000",
"target_document_id": "00000000-0000-4000-8000-000000000000",
"error_message": "string",
"agent_label": "string",
"agent_summary": "string",
"agent_input": "string",
"agent_output": "string",
"text_offset": 0,
"live": {
"choices": [
{
"id": "string",
"intent": "allow_once",
"label": "string"
}
],
"questions": [
{
"id": "string",
"header": "string",
"question": "string",
"options": [
{
"label": "string",
"description": "string"
}
],
"allow_other": false,
"secret": false
}
]
}
}
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/tool-calls/$TOOL/deny" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"choice": "string",
"answers": [
{
"question_id": "string",
"value": {
"label": "string",
"kind": "option"
}
}
]
}'GET/v1/ai/chats/{chat}/attachments/{attachment}
The original bytes, with the stored media type and the file name as an attachment disposition.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_read_attachment
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) | |
| attachmentrequired | path | string (uuid) |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | string (binary)application/octet-stream |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 404 | The attachment, or the stored file, is not there | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 413 | The attachment is larger than the limit | AiFailure |
| 415 | The attachment's type is not allowed, or its content is not what its name says | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
| 507 | The attachment could not be stored on this computer | AiFailure |
curl "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/attachments/$ATTACHMENT" \
-H "Authorization: Bearer $KORU_TOKEN"PUT/v1/ai/chats/{chat}/attachments/{attachment}
The stored attachment's metadata, staged until a send names it. Idempotent by id: the same content answers the same attachment, different content answers attachment_conflict.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_put_attachment
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) | |
| attachmentrequired | path | string (uuid) | |
| namerequired | query | string (min 1 chars) | The file's name, percent-encoded. Checked against the content. |
| sourcerequired | query | "clipboard" | "drop" | "file_picker" |
Request body (required)
application/octet-stream string (binary)
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ChatAttachmentResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 404 | The attachment, or the stored file, is not there | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 413 | The attachment is larger than the limit | AiFailure |
| 415 | The attachment's type is not allowed, or its content is not what its name says | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
| 507 | The attachment could not be stored on this computer | AiFailure |
{
"attachment": {
"id": "00000000-0000-4000-8000-000000000000",
"message_id": "00000000-0000-4000-8000-000000000000",
"ordinal": 0,
"kind": "image",
"source": "clipboard",
"file_name": "string",
"media_type": "string",
"byte_count": 0,
"dimensions": [
{}
],
"origin": {
"workspace_id": "00000000-0000-4000-8000-000000000000",
"document_id": "00000000-0000-4000-8000-000000000000"
},
"reference_path": "string",
"created_at": 0
}
}curl -X PUT "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/attachments/$ATTACHMENT?name=$NAME&source=$SOURCE" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary @fileDELETE/v1/ai/chats/{chat}/attachments/{attachment}
Whether a staged attachment was removed. A sent attachment stays with its message and answers attachment_sent.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_remove_attachment
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) | |
| attachmentrequired | path | string (uuid) |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | RemoveAttachmentResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 404 | The attachment, or the stored file, is not there | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 413 | The attachment is larger than the limit | AiFailure |
| 415 | The attachment's type is not allowed, or its content is not what its name says | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
| 507 | The attachment could not be stored on this computer | AiFailure |
{
"removed": false
}curl -X DELETE "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/attachments/$ATTACHMENT" \
-H "Authorization: Bearer $KORU_TOKEN"POST/v1/ai/chats/{chat}/attachments/reference
The staged path reference. No file or folder contents are read or copied.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_reference_attachment
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json ReferenceAttachmentRequest
| Field | Type | Description |
|---|---|---|
| idrequired | ChatAttachmentId | ChatAttachmentId - a client-minted UUID v4, never derived from content or location. |
| kindrequired | ChatAttachmentKind | |
| pathrequired | string |
{
"id": "00000000-0000-4000-8000-000000000000",
"kind": "image",
"path": "string"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ChatAttachmentResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 404 | The attachment, or the stored file, is not there | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 413 | The attachment is larger than the limit | AiFailure |
| 415 | The attachment's type is not allowed, or its content is not what its name says | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
| 507 | The attachment could not be stored on this computer | AiFailure |
{
"attachment": {
"id": "00000000-0000-4000-8000-000000000000",
"message_id": "00000000-0000-4000-8000-000000000000",
"ordinal": 0,
"kind": "image",
"source": "clipboard",
"file_name": "string",
"media_type": "string",
"byte_count": 0,
"dimensions": [
{}
],
"origin": {
"workspace_id": "00000000-0000-4000-8000-000000000000",
"document_id": "00000000-0000-4000-8000-000000000000"
},
"reference_path": "string",
"created_at": 0
}
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/attachments/reference" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "00000000-0000-4000-8000-000000000000",
"kind": "image",
"path": "string"
}'POST/v1/ai/chats/{chat}/attachments/from-workspace
The staged copy with workspace provenance. The source grant is checked before reading bytes.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_from_workspace_attachment
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) |
Request body (required)
application/json FromWorkspaceAttachmentRequest
| Field | Type | Description |
|---|---|---|
| idrequired | ChatAttachmentId | ChatAttachmentId - a client-minted UUID v4, never derived from content or location. |
| workspacerequired | WorkspaceId | |
| documentrequired | DocumentId | DocumentId - a client-minted v4 UUID, hyphenated and lowercase. Immutable, and never derived from a name or a path. |
{
"id": "00000000-0000-4000-8000-000000000000",
"workspace": "00000000-0000-4000-8000-000000000000",
"document": "00000000-0000-4000-8000-000000000000"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ChatAttachmentResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 404 | The attachment, or the stored file, is not there | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 413 | The attachment is larger than the limit | AiFailure |
| 415 | The attachment's type is not allowed, or its content is not what its name says | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
| 507 | The attachment could not be stored on this computer | AiFailure |
{
"attachment": {
"id": "00000000-0000-4000-8000-000000000000",
"message_id": "00000000-0000-4000-8000-000000000000",
"ordinal": 0,
"kind": "image",
"source": "clipboard",
"file_name": "string",
"media_type": "string",
"byte_count": 0,
"dimensions": [
{}
],
"origin": {
"workspace_id": "00000000-0000-4000-8000-000000000000",
"document_id": "00000000-0000-4000-8000-000000000000"
},
"reference_path": "string",
"created_at": 0
}
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/attachments/from-workspace" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "00000000-0000-4000-8000-000000000000",
"workspace": "00000000-0000-4000-8000-000000000000",
"document": "00000000-0000-4000-8000-000000000000"
}'POST/v1/ai/chats/{chat}/attachments/{attachment}/restage
The new staged attachment. Deleting its id during the copy cancels publication.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_restage_attachment
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) | |
| attachmentrequired | path | string (uuid) |
Request body (required)
application/json RestageAttachmentRequest
| Field | Type | Description |
|---|---|---|
| new_idrequired | ChatAttachmentId | ChatAttachmentId - a client-minted UUID v4, never derived from content or location. |
{
"new_id": "00000000-0000-4000-8000-000000000000"
}Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | ChatAttachmentResponse |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 404 | The attachment, or the stored file, is not there | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 413 | The attachment is larger than the limit | AiFailure |
| 415 | The attachment's type is not allowed, or its content is not what its name says | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
| 507 | The attachment could not be stored on this computer | AiFailure |
{
"attachment": {
"id": "00000000-0000-4000-8000-000000000000",
"message_id": "00000000-0000-4000-8000-000000000000",
"ordinal": 0,
"kind": "image",
"source": "clipboard",
"file_name": "string",
"media_type": "string",
"byte_count": 0,
"dimensions": [
{}
],
"origin": {
"workspace_id": "00000000-0000-4000-8000-000000000000",
"document_id": "00000000-0000-4000-8000-000000000000"
},
"reference_path": "string",
"created_at": 0
}
}curl -X POST "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/attachments/$ATTACHMENT/restage" \
-H "Authorization: Bearer $KORU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"new_id": "00000000-0000-4000-8000-000000000000"
}'GET/v1/ai/chats/{chat}/attachments/{attachment}/thumbnail
A PNG at most 512 pixels on its long edge; attachment_no_preview for anything but a picture.
- Authorization
- Bearer token with
ai.chat - Operation id
- ai_read_attachment_thumbnail
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| chatrequired | path | string (uuid) | |
| attachmentrequired | path | string (uuid) |
Responses
| Status | Description | Body |
|---|---|---|
| 200 | See above. | string (binary)image/png |
| 400 | The request body or path identity is invalid | AiFailure |
| 401 | The gateway bearer is absent or invalid | AiFailure |
| 403 | The bearer lacks the operation's declared AI scope | AiFailure |
| 404 | The attachment, or the stored file, is not there | AiFailure |
| 409 | The operation conflicts with durable chat state | AiFailure |
| 413 | The attachment is larger than the limit | AiFailure |
| 415 | The attachment's type is not allowed, or its content is not what its name says | AiFailure |
| 503 | The AI worker is unavailable or saturated | AiFailure |
| 507 | The attachment could not be stored on this computer | AiFailure |
curl "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/attachments/$ATTACHMENT/thumbnail" \
-H "Authorization: Bearer $KORU_TOKEN"Generated from contract version 1. Last updated .
