AI & OpenAI-compatible
AI query endpoints
Controller: AIController — route prefix api/AI
Authentication: API Key only (x-api-key)
Provider selection — provider vs providerId
| Input | Type | Description |
|---|---|---|
provider | string (optional) | Logical provider type: Azure, Bedrock, Ollama, Gemini, … The platform resolves an active configuration for that type. |
providerId | integer (optional) | Specific provider instance (database row). When present, it takes precedence over provider. The instance must be active and must support the capability required (text, image, document, speech, agent tools, embeddings, realtime). |
JSON and multipart both use camelCase: providerId, not provider_id.
Where providerId is accepted: POST /api/ai/query, /queryImage, /queryDocument, /queryAudio, /chat, and the realtime WebSocket start message.
No-Log Requests
By default every request is fully logged. A request may opt out of logging its prompt and response by setting noLog: true (JSON) or noLog=true (multipart). This is a two-level control:
- Project permission (admin): Allow no-log requests must be enabled on the project.
- Per-request flag: caller sets
noLog: true.
When both are met, the LogEntry row is still created (system prompt + metrics retained) but RequestPrompt / PromptResponse are stored as null.
For OpenAI-compat: { "extension": { "noLog": true } }.
POST /api/ai/query
Text completion / one-shot query.
{
"prompt": "What is the recommended workflow for onboarding?",
"projectId": 1,
"promptId": 5,
"provider": "Azure",
"providerId": 12,
"stream": false,
"externalId": "EMR-12345",
"externalUser": "doctor-smith",
"noLog": false,
"parameters": { "temperature": 0.7, "maxTokens": 500 },
"messageHistory": [
{ "role": "user", "content": "Context from a prior turn" },
{ "role": "assistant", "content": "…" }
]
}
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt | string | ✅ | Text query |
projectId | integer | ❌ | Project (often implied by API key) |
promptId | integer | ❌ | Prompt template |
provider / providerId | string / int | ❌ | Routing (see above) |
stream | boolean | ❌ | SSE streaming (default false) |
externalId / externalUser | string | ❌ | Caller tracking ids |
noLog | boolean | ❌ | See No-Log Requests |
parameters | object | ❌ | temperature, maxTokens, … |
messageHistory | array | ❌ | Prior { role, content } turns |
Success (non-stream):
{
"success": true,
"content": "A typical onboarding workflow includes…",
"errorMessage": null,
"provider": "Azure",
"providerId": 12,
"executionDuration": "00:00:02.345",
"requestId": 12345,
"inputTokens": 25,
"outputTokens": 150
}
Status codes: 200, 400, 401, 403, 500, 502 (provider failure).
curl -X POST https://your-kroov-host/api/ai/query \
-H "x-api-key: your-api-key" \
-H "Content-Type: application/json" \
-d '{ "prompt": "Explain the escalation policy", "projectId": 1, "providerId": 12 }'
POST /api/ai/queryImage
Vision analysis. multipart/form-data.
| Field | Type | Required | Description |
|---|---|---|---|
file | file | ✅ | PNG, JPEG, GIF, WebP, BMP (max ~20 MB) |
prompt | string | ✅ | Question about the image |
projectId, promptId, provider, providerId | ❌ | Routing / templates | |
stream, externalId, externalUser, noLog | ❌ | Same semantics as /query |
curl -X POST https://your-kroov-host/api/ai/queryImage \
-H "x-api-key: your-api-key" \
-F "file=@xray.png" \
-F "prompt=Describe what you see" \
-F "projectId=1" \
-F "providerId=12"
POST /api/ai/queryDocument
Document Q&A. multipart/form-data. Formats: PDF, DOCX, TXT, CSV, XLSX, XLS (max ~50 MB).
| Field | Type | Required | Description |
|---|---|---|---|
file | file | ✅ | Document |
prompt | string | ✅ | Question |
providerId | integer | ❌ | Supported — specific instance |
| Other fields | ❌ | Same pattern as queryImage |
curl -X POST https://your-kroov-host/api/ai/queryDocument \
-H "x-api-key: your-api-key" \
-F "file=@report.pdf" \
-F "prompt=Summarize key points" \
-F "projectId=1" \
-F "providerId=12"
POST /api/ai/queryAudio
Batch transcription (+ optional prompt formatting). multipart/form-data. Formats: MP3, WAV, WebM, OGG, M4A, MP4, FLAC, AAC (max ~100 MB).
| Field | Type | Required | Description |
|---|---|---|---|
file | file | ✅ | Audio |
language | string | ❌ | Omit / "auto" = detect; or fr / en / he |
providerId | integer | ❌ | Instance with speech capability |
enableSpeakerDiarization | boolean | ❌ | Person 1 / Person 2 labels |
usePromptForFormatting | boolean | ❌ | Run formatting prompt after STT |
promptId / transcriptionPromptId / formattingPromptId | int | ❌ | Templates |
projectId, stream, externalId, externalUser, noLog | ❌ | Standard |
Response includes audioDuration (seconds) when available.
WebSocket — Live realtime transcription
Endpoint: wss://{host}/api/ai/realtime/transcribe
Auth: x-api-key query, handshake header, or first message { "type":"auth", "apiKey":"..." }.
Audio: PCM16 mono 24 kHz, ~100 ms base64 chunks.
Client type | Fields | Purpose |
|---|---|---|
auth | apiKey | If not provided via query/header |
start | providerId?, projectId?, language?, externalId?, externalUser? | Open Azure Realtime session |
audio | data (base64 PCM16) | Append chunk |
stop | — | End |
Server type | Meaning |
|---|---|
session_ready | Safe to send audio |
partial / final | Transcript deltas |
speech_started / speech_stopped | VAD |
error / done | Failure / close (logged as audio-realtime) |
Provider config: type AzureRealtime (or Azure with realtime capability), supportsRealtimeAudio, realtimeDeployment (e.g. gpt-realtime).
Batch queryAudio | Live WebSocket | |
|---|---|---|
| Input | File | PCM16 stream |
| Latency | End of file | Partial + final while speaking |
| Capability | SupportsAudio | SupportsRealtimeAudio |
POST /api/ai/chat
Stateful conversational chat with optional MCP agent loop, knowledge-base grounding, attachments, and agent-definition grants.
{
"chatId": null,
"messageUser": "Draft a reply to this customer message",
"messageServer": null,
"projectId": 1,
"promptId": 5,
"providerId": 12,
"stream": true,
"endSession": false,
"externalId": "EMR-12345",
"externalUser": "doctor-smith",
"noLog": false,
"parameters": { "temperature": 0.7, "maxTokens": 500 },
"knowledgeBaseIds": [3, 7],
"agentDefinitionId": 1,
"attachments": [
{
"base64Data": "<base64>",
"contentType": "application/pdf",
"fileName": "note.pdf"
}
],
"delegation": {
"enableRemoteTools": true,
"mcpServerIds": [12],
"mcpInstanceId": null,
"mcpAccountNames": { "12": "default" },
"maxToolRounds": 10,
"maxToolCalls": 10,
"credential": null
}
}
| Parameter | Type | Required | Description |
|---|---|---|---|
messageUser | string | ✅ | User message |
messageServer | string | ❌ | Extra server instructions |
chatId | GUID string | ❌ | Null = new session |
projectId / promptId / provider / providerId | ❌ | Routing | |
stream | boolean | ❌ | SSE (recommended for MCP tools) |
endSession | boolean | ❌ | Close session |
knowledgeBaseIds | int[] | ❌ | Internal KB grounding for a normal text provider (not a KnowledgeBase provider type) |
agentDefinitionId | int | ❌ | Inherit agent MCP allow-list + tool grants; materializes a chat-scoped invocation |
attachments | array | ❌ | Multimodal base64 attachments (audio/image/document) converted server-side |
delegation | object | ❌ | MCP agent loop configuration |
delegation fields:
| Field | Type | Description |
|---|---|---|
enableRemoteTools | boolean | Enable MCP / tool agent loop |
credential | string | Inline JWT / API key / PAT when required |
mcpServerId | int | Single server when project has several |
mcpServerIds | int[] | Multi-MCP; null = unspecified, [] = no MCP (grants only), non-empty = exact set |
mcpInstanceId / mcpInstanceIds | guid / map | Managed worker instance(s) |
mcpAccountNames | map | Per-server vault account name |
maxToolRounds / maxToolCalls | int | Loop limits (default 10) |
internalApiBaseUrl | string | Override for InternalAPI-style MCPs |
contextOverrides | object | Sandbox overrides |
Flow: first message with chatId: null → response includes chatId → subsequent turns reuse it → endSession: true to close.
SSE events (when stream: true and tools enabled): content, toolCalls, status, tokens, error, [DONE].
POST /api/ai/vote/{requestId}
POST /api/ai/vote/chat/{chatId}/{messageIndex}
{ "isThumbsUp": true, "comment": "Accurate and concise" }
Field name is
isThumbsUp(notvote).
GET /api/ai/logs
Project logs visible to the API key (filters: date range, prompt text, externalId, …).
GET /api/ai/assistants?providerId=
List Azure Assistants for a provider.
GET /api/ai/download-blob?blobUrl= / GET /api/ai/view-blob?blobUrl=
Download or inline-view a knowledge-base / storage blob URL previously returned by the platform.
OpenAI-compatible API (/v1)
Controller: OpenAiCompatController — route prefix v1
Authentication: API Key via Authorization: Bearer <api-key> or x-api-key
| Method | Path | Purpose |
|---|---|---|
GET | /v1/models | List models / provider aliases |
GET | /v1/models/{id} | Model detail |
POST | /v1/chat/completions | Chat completions (tools / MCP supported via platform translation) |
POST | /v1/audio/transcriptions | Multipart transcription |
POST | /v1/embeddings | Embeddings (same service as internal RAG) |
Kroov extension on chat completions:
{
"model": "gpt-4o",
"messages": [{ "role": "user", "content": "Hello" }],
"x-kroov": { "noLog": true }
}
Chat Completions compatibility boundary
Kroov guarantees the Chat Completions subset for messages, streaming, structured output,
client tools, and tool_choice. Options outside the implemented request model are rejected with
an OpenAI-shaped invalid_request_error; they are not silently ignored.
| Provider | text | json_object | json_schema | Native mapping |
|---|---|---|---|---|
| OpenAI / Azure OpenAI | Yes | Yes | Yes | OpenAI response_format |
| Gemini | Yes | Yes | Yes | responseMimeType + unmodified responseJsonSchema |
| Anthropic | Yes | No | Yes | output_config.format |
| OpenAI-compatible, Mistral, Moonshot, Z.ai, DeepSeek | Yes | Passthrough | Passthrough | OpenAI response_format; the upstream model may explicitly reject it |
| Bedrock, Ollama, Knowledge Base | Yes | No | No | No equivalent guarantee on this route |
For json_schema, Kroov preserves the complete schema and validates the final non-streaming
response independently before returning success. A schema violation returns
structured_output_validation_failed; for example, {"click":4} is rejected when the schema
requires {"click":{"index":4}}. json_schema with strict:true and stream:true is explicitly
rejected because a complete response cannot be validated before fragments have already been sent.
tool_choice supports none, auto, required, and a named function. OpenAI-shaped providers
receive the original value; Gemini and Anthropic receive their native equivalents. If the selected
provider or model cannot honor an option, the API returns HTTP 400 with code: unsupported_value.
POST /v1/embeddings accepts a string or array of strings and returns float vectors (currently 1536-d for internal RAG).
curl https://your-kroov-host/v1/chat/completions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role":"user","content":"Summarize the product guidelines"}]
}'