Skip to main content
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.

Read non-secret AI provider settings

GET/v1/ai/providers/settings

The selected provider, model, reasoning, endpoint, and credential-presence flag. Secrets and vault labels are never returned.

Authorization
Bearer token with ai.manage_providers
Operation id
ai_provider_settings

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
    }
  ]
}
Example request
curl "http://127.0.0.1:$PORT/v1/ai/providers/settings" \
  -H "Authorization: Bearer $KORU_TOKEN"

Select the active AI provider

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

FieldTypeDescription
providerrequiredAiProviderId

Extensible provider token such as openai, ollama, or koru.

Example request body
{
  "provider": "string"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
    }
  ]
}
Example request
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"
}'

Update non-secret OpenAI settings

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

FieldTypeDescription
modelstring or null
reasoningrequiredReasoningChoice

A provider's reasoning setting - ai.db's nine-token CHECK.

Example request body
{
  "model": "string",
  "reasoning": "default"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "model": "string",
  "reasoning": "default",
  "credential_configured": false
}
Example request
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"
}'

Update Ollama settings

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

FieldTypeDescription
endpointrequiredstring
modelstring or null
reasoningrequiredReasoningChoice

A provider's reasoning setting - ai.db's nine-token CHECK.

Example request body
{
  "endpoint": "string",
  "model": "string",
  "reasoning": "default"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "endpoint": "string",
  "model": "string",
  "reasoning": "default"
}
Example request
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"
}'

Validate an AI provider and selected model

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

NameInTypeDescription
providerrequiredpathstring

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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": {}
  }
}
Example request
curl -X POST "http://127.0.0.1:$PORT/v1/ai/providers/$PROVIDER/validate" \
  -H "Authorization: Bearer $KORU_TOKEN"

List the models a local agent offers

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

NameInTypeDescription
providerrequiredpathstring

Request body (required)

application/json DiscoverAgentModelsRequest

FieldTypeDescription
refreshboolean

Default false

Example request body
{
  "refresh": false
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
}
Example request
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
}'

List what a local agent can be told to do by name in a folder

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

NameInTypeDescription
providerrequiredpathstring

Request body (required)

application/json DiscoverAgentCommandsRequest

FieldTypeDescription
folderstring or null
Example request body
{
  "folder": "string"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "provider": "string",
  "commands": [
    {
      "name": "string",
      "description": "string",
      "display_name": "string",
      "kind": "command",
      "input_hint": "string"
    }
  ]
}
Example request
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"
}'

Pull an Ollama model with bounded progress streaming

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

FieldTypeDescription
modelrequiredstring
Example request body
{
  "model": "string"
}

Responses

StatusDescriptionBody
200See 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
Example request
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"
}'

Cancel the active Ollama model pull

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

StatusDescriptionBody
200See 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
Example 200 response
{
  "cancelled": false
}
Example request
curl -X POST "http://127.0.0.1:$PORT/v1/ai/providers/ollama/pulls/cancel" \
  -H "Authorization: Bearer $KORU_TOKEN"

List AI chats

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

NameInTypeDescription
profilequerystring (uuid)

Only this profile's chats (AI schema 13). Absent lists every profile's.

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
    }
  ]
}
Example request
curl "http://127.0.0.1:$PORT/v1/ai/chats" \
  -H "Authorization: Bearer $KORU_TOKEN"

Create an AI chat

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

FieldTypeDescription
chat_idrequiredChatId

ChatId - a client-minted UUID v4, never derived from content or location.

titlerequiredstring
modelrequiredModelSelection
profile_idProfileId 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.

Example request body
{
  "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

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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"
}'

Clear all AI chat history

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

StatusDescriptionBody
200See 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
Example 200 response
{
  "chats_deleted": 0
}
Example request
curl -X DELETE "http://127.0.0.1:$PORT/v1/ai/chats" \
  -H "Authorization: Bearer $KORU_TOKEN"

Read an authoritative AI chat transcript

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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": []
}
Example request
curl "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT" \
  -H "Authorization: Bearer $KORU_TOKEN"

Rename an AI chat

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json RenameChatRequest

FieldTypeDescription
titlerequiredstring
Example request body
{
  "title": "string"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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 an AI chat while retaining anonymous usage

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "deleted": false
}
Example request
curl -X DELETE "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT" \
  -H "Authorization: Bearer $KORU_TOKEN"

Pin or unpin an AI chat

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json PinChatRequest

FieldTypeDescription
pinnedrequiredboolean
Example request body
{
  "pinned": false
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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
}'

Change the model used by an AI chat

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json UpdateChatModelRequest

FieldTypeDescription
modelrequiredModelSelection
Example request body
{
  "model": {
    "provider": "string",
    "requested_model": "string",
    "default_reasoning": "default"
  }
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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"
  }
}
Example request
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"
  }
}'

Change whether agents and connected assistants can discover a chat

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json UpdateChatPrivacyRequest

FieldTypeDescription
privaterequiredboolean
Example request body
{
  "private": false
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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
}'

Choose the folder a chat's local agent works in

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json UpdateChatFolderRequest

FieldTypeDescription
folderstring or null
Example request body
{
  "folder": "string"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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"
}'

Choose the approval mode a chat's local agent runs its next message in

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json UpdateChatAgentSettingsRequest

FieldTypeDescription
modestring or null

The approval mode, the agent's own id: one it lists in [AgentModelsResponse::modes]. An agent refuses one it does not offer when the message is sent.

fastboolean or null

Fast mode on or off for the chat's model (koru-ai D11), where the model offers it ([AgentModelView::fast]). Applied from the next message, like the mode.

Example request body
{
  "mode": "string",
  "fast": false
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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
}'

List non-private AI chats for a trusted tool caller

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

FieldTypeDescription
cursorstring or null

Default null

page_sizeinteger (uint16, >= 0, <= 65535) or null

Default null

Example request body
{
  "cursor": "string",
  "page_size": 0
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "chats": [
    {
      "chat_id": "00000000-0000-4000-8000-000000000000",
      "title": "string",
      "message_count": 0,
      "created_at": 0,
      "updated_at": 0
    }
  ],
  "next_cursor": "string"
}
Example request
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
}'

Search non-private AI chats for a trusted tool caller

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

FieldTypeDescription
queryrequiredstring
cursorstring or null

Default null

page_sizeinteger (uint16, >= 0, <= 65535) or null

Default null

Example request body
{
  "query": "string",
  "cursor": "string",
  "page_size": 0
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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"
}
Example request
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
}'

Read a non-private AI chat for a trusted tool caller

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

FieldTypeDescription
chat_idrequiredChatId

ChatId - a client-minted UUID v4, never derived from content or location.

cursorstring or null

Default null

page_sizeinteger (uint16, >= 0, <= 65535) or null

Default null

Example request body
{
  "chat_id": "00000000-0000-4000-8000-000000000000",
  "cursor": "string",
  "page_size": 0
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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"
}
Example request
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
}'

Read a chat's workspace-tool policy

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "policy": {
    "chat_id": "00000000-0000-4000-8000-000000000000",
    "mode": "manual",
    "workspace_ids": [
      "00000000-0000-4000-8000-000000000000"
    ]
  }
}
Example request
curl "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/tool-policy" \
  -H "Authorization: Bearer $KORU_TOKEN"

Replace a chat's workspace-tool policy

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json UpdateChatToolPolicyRequest

FieldTypeDescription
moderequiredToolMode
workspace_idsrequiredarray of WorkspaceId
Example request body
{
  "mode": "manual",
  "workspace_ids": [
    "00000000-0000-4000-8000-000000000000"
  ]
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "policy": {
    "chat_id": "00000000-0000-4000-8000-000000000000",
    "mode": "manual",
    "workspace_ids": [
      "00000000-0000-4000-8000-000000000000"
    ]
  }
}
Example request
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"
  ]
}'

Add workspace grants to a chat atomically

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json AddChatWorkspaceGrantsRequest

FieldTypeDescription
workspace_idsrequiredarray of WorkspaceId
Example request body
{
  "workspace_ids": [
    "00000000-0000-4000-8000-000000000000"
  ]
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "policy": {
    "chat_id": "00000000-0000-4000-8000-000000000000",
    "mode": "manual",
    "workspace_ids": [
      "00000000-0000-4000-8000-000000000000"
    ]
  }
}
Example request
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"
  ]
}'

Read private local AI usage

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

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
        }
      }
    ]
  }
}
Example request
curl "http://127.0.0.1:$PORT/v1/ai/usage" \
  -H "Authorization: Bearer $KORU_TOKEN"

Clear private local AI usage and feedback

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

StatusDescriptionBody
200See 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
Example 200 response
{
  "attempts_deleted": 0,
  "feedback_events_deleted": 0
}
Example request
curl -X DELETE "http://127.0.0.1:$PORT/v1/ai/usage" \
  -H "Authorization: Bearer $KORU_TOKEN"

Read current-chat and all private local AI usage

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
        }
      }
    ]
  }
}
Example request
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

FieldTypeDescription
textrequiredstring
limitinteger (uint16, >= 0, <= 65535)

Default 50

cursorstring or null
request_seqinteger (uint64, >= 0)

Default 0

profileProfileId or null

Only this profile's chats (AI schema 13). Absent searches every profile's; each hit names its own.

Example request body
{
  "text": "string",
  "limit": 50,
  "cursor": "string",
  "request_seq": 0,
  "profile": "00000000-0000-4000-8000-000000000000"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
}
Example request
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"
}'

Append response feedback

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

NameInTypeDescription
messagerequiredpathstring (uuid)

Request body (required)

application/json FeedbackRequest

FieldTypeDescription
event_idrequiredFeedbackEventId

FeedbackEventId - a client-minted UUID v4, never derived from content or location.

valuerequiredFeedbackValue
surfacerequiredAiSurface
Example request body
{
  "event_id": "00000000-0000-4000-8000-000000000000",
  "value": "good",
  "surface": "standalone"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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"
}'

Branch an AI chat

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json BranchChatRequest

FieldTypeDescription
branch_chat_idrequiredChatId

ChatId - a client-minted UUID v4, never derived from content or location.

through_message_idrequiredMessageId

MessageId - a client-minted UUID v4, never derived from content or location.

titlerequiredstring
Example request body
{
  "branch_chat_id": "00000000-0000-4000-8000-000000000000",
  "through_message_id": "00000000-0000-4000-8000-000000000000",
  "title": "string"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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": []
  }
}
Example request
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"
}'

Retry a failed AI response

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json RetryTurnRequest

FieldTypeDescription
operation_idrequiredstring
message_idrequiredMessageId

MessageId - a client-minted UUID v4, never derived from content or location.

surfacerequiredAiSurface
explicit_refsarray of ContextRef

Default []

reasoning_overrideReasoningChoice or null
web_searchboolean
replace_pinned_documentsboolean

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.

Example request body
{
  "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

StatusDescriptionBody
200See 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
Example request
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
}'

Store a chat prefix as a Koru document

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json ExportChatRequest

FieldTypeDescription
export_idrequiredDocumentExportId

DocumentExportId - a client-minted UUID v4, never derived from content or location.

idempotency_keyrequiredstring
through_message_idrequiredMessageId

MessageId - a client-minted UUID v4, never derived from content or location.

destinationrequiredDocumentDestination
Example request body
{
  "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

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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"
  }
}'

Read an AI document export

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

NameInTypeDescription
exportrequiredpathstring (uuid)

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
curl "http://127.0.0.1:$PORT/v1/ai/exports/$EXPORT" \
  -H "Authorization: Bearer $KORU_TOKEN"

Commit and stream an AI turn

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json SendTurnRequest

FieldTypeDescription
operation_idrequiredstring
chat_idrequiredChatId

ChatId - a client-minted UUID v4, never derived from content or location.

surfacerequiredAiSurface
markdownrequiredstring
explicit_refsarray of ContextRef

Default []

reasoning_overrideReasoningChoice or null
web_searchboolean
system_prompt_overridestring or null

Debug-only, per-request replacement for Koru's bundled system prompt. Empty or omitted keeps the versioned bundled prompt.

replace_pinned_documentsboolean

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_idsarray 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.

answersNextMessageAnswers 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 - markdown, explicit_refs and attachment_ids empty - and is recorded as no message of the person's: the answers are read on the set's card, and the agent's reply follows the one that asked. user_message_id in the answer is the message the asking reply answered. A set that can no longer be answered is refused (questions_closed).

skillsarray of string

The skills the person picked (koru-ai D121), by the name a local agent lists each under (POST /v1/ai/providers/{provider}/ commands), in the order they appear; markdown says $name where each was. The agent is told each by name beside the text; one it no longer lists is left out, and the text goes as typed. Only an agent chat takes any. Not stored: the message keeps its $name text.

Example request body
{
  "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

StatusDescriptionBody
200See 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
Example request
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"
  ]
}'

Read authoritative AI attempt state

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

NameInTypeDescription
attemptrequiredpathstring (uuid)

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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"
  }
}
Example request
curl "http://127.0.0.1:$PORT/v1/ai/attempts/$ATTEMPT" \
  -H "Authorization: Bearer $KORU_TOKEN"

Cancel an AI attempt

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

NameInTypeDescription
attemptrequiredpathstring (uuid)

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "attempt_id": "00000000-0000-4000-8000-000000000000",
  "status": "pending"
}
Example request
curl -X POST "http://127.0.0.1:$PORT/v1/ai/attempts/$ATTEMPT/cancel" \
  -H "Authorization: Bearer $KORU_TOKEN"

Send a message into a running agent reply

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

NameInTypeDescription
attemptrequiredpathstring (uuid)

Request body (required)

application/json SteerAttemptRequest

FieldTypeDescription
operation_idrequiredstring (uuid)
markdownrequiredstring
Example request body
{
  "operation_id": "00000000-0000-4000-8000-000000000000",
  "markdown": "string"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "attempt_id": "00000000-0000-4000-8000-000000000000",
  "user_message_id": "00000000-0000-4000-8000-000000000000",
  "replayed": false
}
Example request
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"
}'

Approve a pending workspace-tool call

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

NameInTypeDescription
toolrequiredpathstring (uuid)

Request body (required)

application/json ToolDecisionRequest

FieldTypeDescription
choicestring or null
answersarray of AgentAnswer or null
Example request body
{
  "choice": "string",
  "answers": [
    {
      "question_id": "string",
      "value": {
        "label": "string",
        "kind": "option"
      }
    }
  ]
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
        }
      ]
    }
  }
}
Example request
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"
      }
    }
  ]
}'

Deny a pending workspace-tool call

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

NameInTypeDescription
toolrequiredpathstring (uuid)

Request body (required)

application/json ToolDecisionRequest

FieldTypeDescription
choicestring or null
answersarray of AgentAnswer or null
Example request body
{
  "choice": "string",
  "answers": [
    {
      "question_id": "string",
      "value": {
        "label": "string",
        "kind": "option"
      }
    }
  ]
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
        }
      ]
    }
  }
}
Example request
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"
      }
    }
  ]
}'

Read an attachment's stored original

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

NameInTypeDescription
chatrequiredpathstring (uuid)
attachmentrequiredpathstring (uuid)

Responses

StatusDescriptionBody
200See 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
Example request
curl "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/attachments/$ATTACHMENT" \
  -H "Authorization: Bearer $KORU_TOKEN"

Store a picture or file for a local agent chat's next message

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

NameInTypeDescription
chatrequiredpathstring (uuid)
attachmentrequiredpathstring (uuid)
namerequiredquerystring (min 1 chars)

The file's name, percent-encoded. Checked against the content.

sourcerequiredquery"clipboard" | "drop" | "file_picker"

Request body (required)

application/octet-stream string (binary)

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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 @file

Remove a staged attachment

DELETE/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

NameInTypeDescription
chatrequiredpathstring (uuid)
attachmentrequiredpathstring (uuid)

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "removed": false
}
Example request
curl -X DELETE "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/attachments/$ATTACHMENT" \
  -H "Authorization: Bearer $KORU_TOKEN"

Stage a local file or folder path for a local agent chat

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json ReferenceAttachmentRequest

FieldTypeDescription
idrequiredChatAttachmentId

ChatAttachmentId - a client-minted UUID v4, never derived from content or location.

kindrequiredChatAttachmentKind
pathrequiredstring
Example request body
{
  "id": "00000000-0000-4000-8000-000000000000",
  "kind": "image",
  "path": "string"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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"
}'

Copy a granted workspace file into a local agent chat

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

NameInTypeDescription
chatrequiredpathstring (uuid)

Request body (required)

application/json FromWorkspaceAttachmentRequest

FieldTypeDescription
idrequiredChatAttachmentId

ChatAttachmentId - a client-minted UUID v4, never derived from content or location.

workspacerequiredWorkspaceId
documentrequiredDocumentId

DocumentId - a client-minted v4 UUID, hyphenated and lowercase. Immutable, and never derived from a name or a path.

Example request body
{
  "id": "00000000-0000-4000-8000-000000000000",
  "workspace": "00000000-0000-4000-8000-000000000000",
  "document": "00000000-0000-4000-8000-000000000000"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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"
}'

Copy a sent attachment into a fresh staged attachment for recall

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

NameInTypeDescription
chatrequiredpathstring (uuid)
attachmentrequiredpathstring (uuid)

Request body (required)

application/json RestageAttachmentRequest

FieldTypeDescription
new_idrequiredChatAttachmentId

ChatAttachmentId - a client-minted UUID v4, never derived from content or location.

Example request body
{
  "new_id": "00000000-0000-4000-8000-000000000000"
}

Responses

StatusDescriptionBody
200See 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
Example 200 response
{
  "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
  }
}
Example request
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"
}'

Read a picture attachment's thumbnail

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

NameInTypeDescription
chatrequiredpathstring (uuid)
attachmentrequiredpathstring (uuid)

Responses

StatusDescriptionBody
200See 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
Example request
curl "http://127.0.0.1:$PORT/v1/ai/chats/$CHAT/attachments/$ATTACHMENT/thumbnail" \
  -H "Authorization: Bearer $KORU_TOKEN"
Streaming API reference (AsyncAPI) Some operations here answer with a stream. The streaming reference documents the AsyncAPI channels and their messages.

Generated from contract version 1. Last updated .

Privacy choices

Choose which optional tools Koru may use on this site. Your choice is saved in a cookie in this browser for 12 months. Privacy policy

Privacy choices

Keep you signed in, protect forms and remember your language and this choice.

Always on

PostHog and Google Analytics 4 measure page views and actions such as downloads, using cookies or local storage.

PostHog records how pages are used, with form inputs masked and private pages excluded. Needs Analytics.

Ahrefs Web Analytics measures visits and where they come from, and Google Ads measures whether our ads led to a visit or download.