Skip to main content

This is the single-file HTTP API contract for teams that integrate Kroov into another application. The same document lives in the repository as docs/API-INTEGRATION-GUIDE.md. The other pages in this API section are the same contract split for browsing.

API Integration Guide

Kroov Platform


Document Version: 2.0
Date: August 2026
Status: Production-Ready
Classification: Public - Developer Documentation


Document Purpose

This guide provides comprehensive documentation for developers integrating applications with the Kroov Platform. It covers the full HTTP API surface as of August 2026, including:

  • Authentication methods (API key, JWT, OAuth, delegation)
  • Feature flags and deployment capability discovery
  • AI request, chat, multimodal, streaming, and OpenAI-compatible endpoints
  • Projects, prompts, providers, cost, and dashboard APIs
  • Internal Knowledge Bases (pgvector RAG) and document ingestion
  • MCP control plane (servers, catalog, definitions, instances, runtimes, proxy)
  • Configurable Agents, environments, presets, headless runs, and interactive sessions
  • Autonomous scheduled tasks
  • Event automations, Pipedream connections, approvals, and signed webhooks
  • GitHub App integration, federation, and security endpoints
  • Request/response formats, SSE/WebSocket streaming, errors, and best practices

Audience: Application developers, integration teams, third-party vendors, and platform operators (EMR and other clinical systems are optional integration examples)

Related deep-dive docs (architecture / ops — this guide remains the API contract):

TopicDocument
Internal RAG / pgvectordocs/INTERNAL-RAG.md
Configurable agents & sessionsdocs/CONFIGURABLE-AGENTS.md
Scheduled tasksdocs/AUTONOMOUS-SCHEDULED-TASKS.md
Automations walkthroughdocs/automations-demo.md
MCP on Kubernetesdocs/MCP-KUBERNETES.md
Native WhatsApp Businessdocs/WHATSAPP-BUSINESS.md
Native Telegramdocs/TELEGRAM.md
Personal assistantdocs/PERSONAL-ASSISTANT.md

Table of Contents

  1. Getting Started
  2. Authentication
  3. Feature Flags & Capability Discovery
  4. API Endpoints Reference
  5. Request & Response Formats
  6. Streaming API
  7. Error Handling
  8. Code Examples
  9. Best Practices
  10. Security Considerations
  11. Troubleshooting
  12. Appendices

1. Getting Started

1.1 Base URL

Production / staging (placeholder):

{baseUrl}/api

or

https://your-kroov-host/api

Local:

https://localhost:7003/api

WebApp (Vite): https://localhost:3007 — proxies /api:7003.

Note: Many deployments are reachable only from a private network. Public Internet access is usually disabled unless federation or a public webhook base URL is intentionally configured.

OpenAI-compatible routes live under /v1 (not /api/v1):

{baseUrl}/v1/chat/completions

or https://your-kroov-host/v1/chat/completions.

Liveness probe (anonymous): GET /health{ "status": "healthy" }.

1.2 API Versioning

Current Version: v1 (implicit for /api/*; explicit OpenAI surface at /v1)

Future breaking changes may introduce /api/v2/. Until then, additive fields are preferred; clients should ignore unknown JSON properties.

1.3 Content Types

Request:

  • application/json — JSON bodies (property names are camelCase)
  • multipart/form-data — file uploads (images, documents, audio, KB documents, session files)

Response:

  • application/json — standard responses
  • text/event-stream — Server-Sent Events (chat agent loop, agent runs, sessions)
  • WebSocket JSON frames — live realtime transcription

1.4 Auth at a glance

AudienceSchemeHeader / mechanism
EMR / backend apps calling AIAPI Keyx-api-key: <key>
OpenAI SDK clientsAPI Key as BearerAuthorization: Bearer <api-key> on /v1/*
SPA / admin / agents / automationsJWTAuthorization: Bearer <jwt>
Federated MCP callers (e.g. Eva)DelegationX-AINexus-Delegation: <token>
Pipedream webhooksHMACx-pd-signature on POST /api/integration-events/...
MCP runtime proxy (internal)Controller tokenX-Mcp-Controller-Token

Important: Native AI endpoints under /api/ai/* accept API Key authentication only (AuthenticationSchemes = "ApiKey"). JWT works for admin/SPA controllers, not for /api/ai/query or /api/ai/chat.

1.5 Quick Start Checklist

  1. Obtain an API key (or JWT for admin surfaces) from a platform administrator
  2. Identify your project ID
  3. Call GET /api/features to learn which optional capabilities this deployment exposes
  4. Test in development first
  5. Prefer providerId (specific configured instance) over bare provider type strings

First AI call (API key):

curl -X POST https://your-kroov-host/api/ai/query \
-H "x-api-key: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Hello, this is a test query",
"projectId": 1
}'

Expected response:

{
"success": true,
"content": "Hello! How can I assist you today?",
"provider": "Azure",
"providerId": 12,
"executionDuration": "00:00:01.234",
"requestId": 12345,
"inputTokens": 10,
"outputTokens": 15
}

Local admin login → JWT (Development with Authentication__EnableLocalLogin=true):

curl -s -X POST https://localhost:7003/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin"}'

1.6 Interactive API Documentation

Swagger UI (Development environment only):

{baseUrl}/swagger
https://localhost:7003/swagger

Configure ApiKey (x-api-key) and/or Bearer JWT in the Authorize dialog.


2. Authentication

2.1 Authentication Methods

MethodTypical use
API KeyApplication integrations, EMR, OpenAI-compat clients
JWT BearerWebApp, admin CRUD, agents, sessions, automations
Google OAuthInteractive SPA login (DefaultLoginMode=Google in many deploys)
Delegation tokenExternal services acting for a federated user on MCP instance APIs
Local loginDev / testing only (Authentication:EnableLocalLogin)

Discover which login modes are enabled:

curl -s https://your-kroov-host/api/auth/options
{
"enableLocalLogin": false,
"enableGoogleLogin": true,
"googleOAuthConfigured": true,
"defaultLoginMode": "Google"
}

2.2 API Key Authentication

  1. An administrator creates a key via POST /api/auth/apikey (or project rotate-key flows)
  2. The key may be scoped to a project
  3. Clients send x-api-key: <secret> on every request
  4. On /v1/*, Authorization: Bearer <api-key> is also accepted (rewritten to ApiKey)
POST /api/ai/query HTTP/1.1
Host: your-kroov-host
x-api-key: ainx_...
Content-Type: application/json

{ "prompt": "Your query here", "projectId": 1 }

Security:

  • Store keys in a secrets manager / environment variables — never in source control
  • Rotate periodically; use separate keys for dev and production
  • Never put keys in URL query strings except for WebSocket clients that cannot set headers (prefer first-message auth when possible)
  • Never log raw keys

Create / list API keys (JWT required)

# Create — secret returned once
curl -X POST https://your-kroov-host/api/auth/apikey \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{ "name": "EMR Integration", "projectId": 1, "expiresAt": null }'

# List summaries (no secrets)
curl "https://your-kroov-host/api/auth/apikey?projectId=1" \
-H "Authorization: Bearer $JWT"
{
"key": "ainx_...",
"name": "EMR Integration",
"expiresAt": null
}

2.3 JWT Authentication

Obtain via local login (dev):

curl -X POST https://your-kroov-host/api/auth/login \
-H "Content-Type: application/json" \
-d '{ "username": "your-username", "password": "your-password" }'

Google OAuth: GET /api/auth/google (browser redirect).

Refresh / logout:

curl -X POST .../api/auth/refresh -H 'Content-Type: application/json' \
-d '{ "refreshToken": "..." }'
curl -X POST .../api/auth/logout -H 'Content-Type: application/json' \
-d '{ "refreshToken": "..." }'

Using JWT:

GET /api/project HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Typical lifetimes: access token ~60 minutes; refresh token ~7 days (deployment-configurable).

Admin policy (AdminOnly): JWT Admin role, or Google admin email allow-list. Many write endpoints require it.

2.4 Google OAuth

  • GET /api/auth/google — start Google OAuth (browser redirect)

SPA token hand-off (useful in headless / local testing): navigate to
/login?token=<JWT>&refreshToken=<REFRESH> after calling the login API.

2.5 Delegation tokens (federation)

External apps exchange verified service + user identity for a short-lived X-AINexus-Delegation token used with MCP instance APIs and chat:

curl -X POST https://your-kroov-host/api/delegations/exchange \
-H "x-api-key: PROJECT_API_KEY" \
-H "X-AINexus-Service-Token: <gcp-sa-jwt>" \
-H "X-AINexus-User-Token: <google-user-jwt>" \
-H "Content-Type: application/json" \
-d '{ "phoneProof": "<optional-eva-phone-jwt>", "scopes": ["mcp:pair", "mcp:invoke", "mcp:manage"] }'
{ "token": "...", "tokenType": "Kroov-Delegation", "expiresIn": 300 }

See §4.4 Federation and §4.8 MCP.

2.6 Global authorization notes

  • The API applies a fallback RequireAuthenticatedUser policy: controllers must explicitly [AllowAnonymous] for public routes (auth options, features, webhooks, health, CSP report, MCP proxy with controller token, etc.).
  • JSON uses camelCase everywhere.
  • Multipart field names match property names (providerId, projectId, noLog, …).

3. Feature Flags & Capability Discovery

Optional product surfaces are gated by configuration. Always call this before assuming Agents / Sessions / Automations / hosted MCP are available.

GET /api/features — AllowAnonymous

curl -s https://localhost:7003/api/features
{
"hostedMcpRuntimes": true,
"kubernetesAgents": true,
"agentSessions": true,
"scheduledTasks": true,
"automations": true,
"isDevelopment": true,
"pipedreamConfigured": false
}
Response fieldTrue when
hostedMcpRuntimesKubernetes:Enabled
kubernetesAgentsAgents:Enabled and Kubernetes:Enabled
agentSessionsAgentSessions:Enabled and Kubernetes:Enabled
scheduledTasksScheduledTasks:Enabled and sessions (above)
automationsAutomations:Enabled only — does not require Kubernetes
isDevelopmentHosting environment is Development
pipedreamConfiguredPipedream OAuth (ClientId+ClientSecret+ProjectId) or legacy ApiKey is set

HTTP 503 is returned by gated controllers when a feature is off (automations, scheduled tasks create/update, hosted MCP attribute, internal KB when disabled / non-PostgreSQL, agent runs when Agents disabled, etc.).

CapabilityNeeds K8s?Config keys
Hosted MCP instances / runtime proxyYesKubernetes:Enabled, controller token
Headless agent Jobs (POST /api/agents/{id}/runs)YesAgents:Enabled
Interactive sessionsYesAgentSessions:Enabled
Scheduled tasksYes (via sessions)ScheduledTasks:Enabled
Event automationsNo (in-process Service runtime)Automations:Enabled, Automations:PublicWebhookBaseUrl
Internal Knowledge BasesPostgreSQL + pgvector + object storageKnowledgeBase:Internal:Enabled

4. API Endpoints Reference

4.1 AI Query Endpoints

Controller: AIController — route prefix api/AI
Authentication: API Key only (x-api-key)

Provider selection — provider vs providerId

InputTypeDescription
providerstring (optional)Logical provider type: Azure, Bedrock, Ollama, Gemini, … The platform resolves an active configuration for that type.
providerIdinteger (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:

  1. Project permission (admin): Allow no-log requests must be enabled on the project.
  2. 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": "…" }
]
}
ParameterTypeRequiredDescription
promptstringText query
projectIdintegerProject (often implied by API key)
promptIdintegerPrompt template
provider / providerIdstring / intRouting (see above)
streambooleanSSE streaming (default false)
externalId / externalUserstringCaller tracking ids
noLogbooleanSee No-Log Requests
parametersobjecttemperature, maxTokens, …
messageHistoryarrayPrior { 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.

FieldTypeRequiredDescription
filefilePNG, JPEG, GIF, WebP, BMP (max ~20 MB)
promptstringQuestion about the image
projectId, promptId, provider, providerIdRouting / templates
stream, externalId, externalUser, noLogSame 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).

FieldTypeRequiredDescription
filefileDocument
promptstringQuestion
providerIdintegerSupported — specific instance
Other fieldsSame 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).

FieldTypeRequiredDescription
filefileAudio
languagestringOmit / "auto" = detect; or fr / en / he
providerIdintegerInstance with speech capability
enableSpeakerDiarizationbooleanPerson 1 / Person 2 labels
usePromptForFormattingbooleanRun formatting prompt after STT
promptId / transcriptionPromptId / formattingPromptIdintTemplates
projectId, stream, externalId, externalUser, noLogStandard

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 typeFieldsPurpose
authapiKeyIf not provided via query/header
startproviderId?, projectId?, language?, externalId?, externalUser?Open Azure Realtime session
audiodata (base64 PCM16)Append chunk
stopEnd
Server typeMeaning
session_readySafe to send audio
partial / finalTranscript deltas
speech_started / speech_stoppedVAD
error / doneFailure / close (logged as audio-realtime)

Provider config: type AzureRealtime (or Azure with realtime capability), supportsRealtimeAudio, realtimeDeployment (e.g. gpt-realtime).

Batch queryAudioLive WebSocket
InputFilePCM16 stream
LatencyEnd of filePartial + final while speaking
CapabilitySupportsAudioSupportsRealtimeAudio

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
}
}
ParameterTypeRequiredDescription
messageUserstringUser message
messageServerstringExtra server instructions
chatIdGUID stringNull = new session
projectId / promptId / provider / providerIdRouting
streambooleanSSE (recommended for MCP tools)
endSessionbooleanClose session
knowledgeBaseIdsint[]Internal KB grounding for a normal text provider (not a KnowledgeBase provider type)
agentDefinitionIdintInherit agent MCP allow-list + tool grants; materializes a chat-scoped invocation
attachmentsarrayMultimodal base64 attachments (audio/image/document) converted server-side
delegationobjectMCP agent loop configuration

delegation fields:

FieldTypeDescription
enableRemoteToolsbooleanEnable MCP / tool agent loop
credentialstringInline JWT / API key / PAT when required
mcpServerIdintSingle server when project has several
mcpServerIdsint[]Multi-MCP; null = unspecified, [] = no MCP (grants only), non-empty = exact set
mcpInstanceId / mcpInstanceIdsguid / mapManaged worker instance(s)
mcpAccountNamesmapPer-server vault account name
maxToolRounds / maxToolCallsintLoop limits (default 10)
internalApiBaseUrlstringOverride for InternalAPI-style MCPs
contextOverridesobjectSandbox 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 (not vote).


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.


4.2 OpenAI-Compatible API (/v1)

Controller: OpenAiCompatController — route prefix v1
Authentication: API Key via Authorization: Bearer <api-key> or x-api-key

MethodPathPurpose
GET/v1/modelsList models / provider aliases
GET/v1/models/{id}Model detail
POST/v1/chat/completionsChat completions (tools / MCP supported via platform translation)
POST/v1/audio/transcriptionsMultipart transcription
POST/v1/embeddingsEmbeddings (same service as internal RAG)

Kroov extension on chat completions:

{
"model": "gpt-4o",
"messages": [{ "role": "user", "content": "Hello" }],
"extension": { "noLog": true }
}

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"}]
}'

4.3 Chat History

User-scoped — ChatController (api/Chat) — JWT or API Key

MethodPathPurpose
GET/api/chat/sessions?externalUser=&externalId=&includeInactive=List sessions for caller identity
GET/api/chat/conversation/{chatId}?externalUser=&externalId=Full Q/A transcript

Admin — ChatAdminController (api/ChatAdmin) — AdminOnly

MethodPathPurpose
GET/api/chatadmin/sessions?projectId=&externalUser=&externalId=Cross-user listing
GET/api/chatadmin/conversation/{chatId}Ops transcript

4.4 Projects, Members & Federation

Projects — ProjectController (api/Project) — JWT

MethodPathAuthPurpose
GET/api/project?includeSpent=AuthList projects
GET/api/project/{id}AuthDetail
POST/api/projectAdminCreate
PUT/api/project/{id}AdminUpdate
DELETE/api/project/{id}AdminDelete
POST/api/project/{id}/rotate-keyAdminRotate project API key
GET/api/project/{id}/membersProject accessMembers
POST/api/project/{id}/membersAdmin/Owner{ "username", "role?" }
DELETE/api/project/{id}/members/{userId}Admin/OwnerRemove
POST/api/project/spentAuthBody int[] → spent by project
{
"name": "Operations",
"allowedAdGroups": null,
"allowedAdUsers": null,
"budget": 1000,
"allowNoLog": false
}

4.4.4 Federation — MCP entitlements

Controller: ProjectFederationControllerapi/projects/{projectId}/federationAdminOnly

MethodPathPurpose
GET.../external-principalsList external principals
POST.../identity-providersRegister IdP (google-user, eva-phone, …)
POST.../service-identitiesRegister service identity (e.g. Cloud Run SA)
POST.../mcp-entitlementsGrant MCP entitlement
GET.../mcp-entitlementsList
DELETE.../mcp-entitlements/{id}Revoke
GET.../internal-usersInternal users

Entitlement body example: { "mcpServerId": 5, "userId": null, "externalPrincipalId": "…", "maxInstances": 1 }.

Delegation exchange: POST /api/delegations/exchange — see §2.5.


4.5 Prompts

Controller: PromptController (api/Prompt) — JWT; mutations Admin

MethodPathAuthPurpose
GET/api/prompt/project/{projectId}AuthList
GET/api/prompt/project/{projectId}/activeAuthActive prompt
GET/api/prompt/{id}AuthGet
POST/api/promptAdminCreate
PUT/api/prompt/{id}AdminUpdate
POST/api/prompt/{id}/activateAdminActivate
DELETE/api/prompt/{id}AdminDelete
GET/api/prompt/{id}/versionsAuthHistory
POST/api/prompt/{id}/restore/{versionId}AdminRestore
{
"projectId": 1,
"name": "Intake",
"systemPrompt": "You are an intake assistant…",
"userPrompt": null,
"isActive": true,
"temperature": 0.3,
"maxTokens": 2000
}

4.6 Providers

Controller: ProviderController (api/Provider) — JWT; writes Admin

MethodPathPurpose
GET/api/providerList
GET/api/provider/{id}Detail
POST / PUT / DELETE/api/provider[/{id}]CRUD (Admin)
PATCH/api/provider/{id}/statusEnable/disable
POST/api/provider/{id}/set-defaultSet default
POST/api/provider/discover-modelsDiscover models
GET/api/provider/{id}/modelsModels
GET/api/provider/{id}/usage / /api/provider/usageUsage

Types include: Azure, Bedrock, Gemini, Ollama, OpenAI-compatible, KnowledgeBase (KbBackend: AzureSearch | Internal), AzureRealtime.

Capability flags: text / audio / document / image / agentTools / embeddings / realtime audio.

For Internal RAG: enable Embeddings on an Azure or Gemini provider, create a Knowledge Base, then create a KnowledgeBase provider with KbBackend=Internal linked to that base — or ground chat via knowledgeBaseIds on /api/ai/chat.


4.7 Knowledge Bases

Kroov supports internal knowledge bases backed by PostgreSQL pgvector (and durable object storage: Azure Blob or GCS). Azure AI Search remains available as a provider KbBackend.

Runtime requirements: PostgreSQL + vector extension, KnowledgeBase:Internal:Enabled, embedding provider (Azure/Gemini with SupportsEmbeddings), storage provider configured. Otherwise KB admin APIs return 503.

Deep dive: docs/INTERNAL-RAG.md.

Admin CRUD — KnowledgeBasesController (api/knowledge-bases) — AdminOnly

MethodPathPurpose
GET/api/knowledge-bases?projectId=List
GET/api/knowledge-bases/{id}Detail
POST/api/knowledge-basesCreate
PUT/api/knowledge-bases/{id}Update
DELETE/api/knowledge-bases/{id}Delete base + storage + chunks
PUT/api/knowledge-bases/{id}/projectsShare with additional projects { "projectIds": [1,2] }
GET/api/knowledge-bases/{id}/documents?page&pageSize&search&statusList documents
POST/api/knowledge-bases/{id}/documentsUpload (multipart, field files) — pdf/docx/xlsx/xls/csv/txt
POST/api/knowledge-bases/{id}/documents/{documentId}/reindexReindex
GET/api/knowledge-bases/{id}/documents/{documentId}/contentDownload original
DELETE/api/knowledge-bases/{id}/documents/{documentId}Delete document

Create / update body:

{
"projectId": 1,
"name": "Team Protocols",
"embeddingProviderId": 3,
"embeddingModel": "text-embedding-3-small",
"chunkSize": 1000,
"chunkOverlap": 100,
"isActive": true
}

Document lifecycle: PendingExtractingEmbeddingIndexed (or failed with error; reindex supported). Ingestion uses FOR UPDATE SKIP LOCKED leases across API replicas.

Upload example:

curl -X POST https://your-kroov-host/api/knowledge-bases/3/documents \
-H "Authorization: Bearer $ADMIN_JWT" \
-F "files=@protocol.pdf" \
-F "files=@checklist.docx"

Project picker — ProjectKnowledgeBasesController

MethodPathAuthPurpose
GET/api/projects/{projectId}/knowledge-basesJWT (project access)Read-only list of own + shared bases for chat/agent UI

Using knowledge in inference

  1. KnowledgeBase provider — create a provider of type KnowledgeBase with KbBackend=Internal and internalKnowledgeBaseId; query it like any other provider.
  2. Chat grounding — on POST /api/ai/chat, pass knowledgeBaseIds: […] with a normal text model; retrieved chunks are injected as server context with [ref_id:N] citation markers.
  3. Embeddings APIPOST /v1/embeddings for the same embedding pipeline.
  4. Scheduled tasks / sessions — pass allow-listed knowledgeBaseIds so knowledge_base_search may only search those bases (empty list = no restriction).

Blob URLs in citations are opened via /api/ai/download-blob or /api/ai/view-blob with the API key.


4.8 MCP Platform

Kroov exposes a full MCP control plane: remote HTTP MCPs, catalog install, Kubernetes-backed managed runtimes, per-user OAuth vault, federation entitlements, definitions/diagnostics, and an internal runtime proxy.

SurfaceAuth
api/mcp-servers, definitions, credentialsJWT (writes mostly Admin)
api/mcp-catalog, api/mcp-runtimeJWT Admin (+ hosted runtime attribute)
api/mcp-instancesJWT or delegation; requires hosted MCP runtimes
api/integrationsJWT (OAuth connect for end users)
api/mcp-runtime-proxyX-Mcp-Controller-Token (AllowAnonymous + token check)
api/delegations/exchangeProject API key + service/user tokens
Chat with MCP toolsProject API key (+ optional user/delegation headers)

Concepts

Transport types (transportType):

ValueNameUsage
0HttpOpenApiDeprecated — OpenAPI bridge
1McpStreamableHttpNative MCP over HTTP (preferred)
2McpStdioStdio profile (catalog custom images; not JSON-importable)

Credential types (credentialType): None, Passthrough, OAuth, Pat, EnvVar.

Remote vs managed: Remote MCPs call a vendor URL. Managed MCPs run as isolated Kubernetes worker pods; chat targets mcpInstanceId whose endpoint is routed through the internal proxy.

MCP servers — McpServerController (api/mcp-servers)

MethodPathAuthPurpose
GET/api/mcp-serversAuthList (non-admins: active only)
GET/api/mcp-servers/{id}AuthDetail + tools
POST / PUT / DELETE/api/mcp-servers[/{id}]AdminCRUD
PUT/api/mcp-servers/{id}/tools/{toolId}AdminEnable/disable tool
POST/api/mcp-servers/import/previewAdminPreview JSON config
POST/api/mcp-servers/importAdminInstall from JSON (+ optional one-shot credential)
POST/api/mcp-servers/{id}/sync-toolsAdminNative MCP tool discovery
POST/api/mcp-servers/{id}/import-openapiAdminLegacy HttpOpenApi
GET / PUT/api/projects/{projectId}/mcp-serversAuth / AdminProject assignments
PUT/api/mcp-servers/{id}/projectsAdminSet this server’s projects (non-destructive)

Profile fields: name, baseUrl, description, transportType, remoteEndpointUrl, registryServerName, registryVersion, stdioCommand, stdioArgsJson, stdioEnvVarKeysJson, authMode, contextMode, requireCredential, credentialType, oauthProviderKey, authHeaderName, authHeaderTemplate (must contain {credential} when required), allowedHosts, allowedPathPrefix, defaultMaxToolRounds, defaultMaxToolCalls, isActive.

Remote Streamable HTTP create:

curl -X POST https://your-kroov-host/api/mcp-servers \
-H "Authorization: Bearer $ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{
"name": "SerpAPI",
"baseUrl": "https://mcp.serpapi.com/{credential}/mcp",
"transportType": 1,
"remoteEndpointUrl": "https://mcp.serpapi.com/{credential}/mcp",
"credentialType": 3,
"requireCredential": true,
"authMode": 2
}'

JSON import (Claude Desktop / Cursor / VS Code mcpServers shape). Stdio/npx entries are rejected for JSON import — use Catalog → Custom for containerised MCPs.

{
"json": "{ \"mcpServers\": { \"gmail\": { \"serverUrl\": \"https://gmailmcp.googleapis.com/mcp/v1\", \"oauth\": { \"clientId\": \"…\", \"clientSecret\": \"…\" } } } }",
"credential": "optional-one-shot-pat"
}

Sync tools:

curl -X POST .../api/mcp-servers/12/sync-tools \
-H "Authorization: Bearer $ADMIN_JWT" \
-H "Content-Type: application/json" \
-d '{ "credential": "sk-live-..." }'

{ "synced": 15 }

Catalog — McpCatalogController (api/mcp-catalog) — AdminOnly

source query: github (default) or custom (pre-built K8s images).

MethodPathPurpose
GET/api/mcp-catalog?search=&source=&page=&pageSize=Search (auto-sync)
GET/api/mcp-catalog/{serverName}?source=Detail
POST/api/mcp-catalog/refresh?source=Force sync
POST/api/mcp-catalog/{serverName}/install?source=Install (+ tool sync for remote)

Custom install may return managedRuntime: true, runtimeProfileId, requiresPairing.

Definitions & credentials — McpDefinitionController

Under /api/mcp-servers/{id}/…:

MethodPathPurpose
GET / PUT/definitionCanonical definition
POST/definition/validateValidate
GET/definition/export?format=Export
GET/definition/revisions[/{rev}]History
POST/definition/revisions/{rev}/restoreRestore
GET/credential-requirementsWhat creds are needed
GET/credential-statusStatus for account/project
PUT / DELETE/credentials/{accountName}Vault set/delete
POST/diagnostics/{validate|discover-oauth|test-connection|list-tools}Admin diagnostics

Managed instances — McpInstanceController (api/mcp-instances)

Requires hosted MCP runtimes (Kubernetes:Enabled). Attribute [RequiresHostedMcpRuntimes]503 otherwise.

MethodPathPurpose
GET / POST/api/mcp-instancesList / create (Idempotency-Key optional)
GET/{id}Detail + availableActions
GET/{id}/pairingWhatsApp QR (Cache-Control: no-store)
POST/{id}/restart | /suspend | /resume | /reassociateLifecycle
GET / POST / DELETE/{id}/grants[/{grantId}]Instance grants
DELETE/{id}Delete worker + row
POST/{id}/runtime-statusAllowAnonymous — worker → API status callback
{ "mcpServerId": 5, "projectId": 1, "ownerUserId": 2, "ownerExternalPrincipalId": null }

Statuses: Requested, Provisioning, AwaitingPairing, Ready, Degraded, Suspended, PairingRejected, Deleting, Deleted.

Federated callers use X-AINexus-Delegation with scopes such as mcp:pair, mcp:invoke, mcp:manage.

Runtime profiles — McpRuntimeController (api/mcp-runtime) — Admin + hosted runtimes

MethodPathPurpose
POST/api/mcp-runtime/analyzePreview npx/stdio → generic runtime
POST/api/mcp-runtime/registerRegister (hostingMode: container | subprocess Dev-only)
GET/api/mcp-runtime/{id}Profile status
POST/api/mcp-runtime/{id}/rebuildKaniko rebuild

Runtime proxy — McpRuntimeProxyController (api/mcp-runtime-proxy)

Internal data-plane proxy to the worker pod via the Kubernetes API:

GET|POST|DELETE /api/mcp-runtime-proxy/{instanceId}/mcp/

Auth: header X-Mcp-Controller-Token must equal Kubernetes:ControllerToken / McpRuntime:ControllerToken.

User OAuth / PAT integrations — IntegrationAuthController (api/integrations)

MethodPathPurpose
GET/api/integrations/mineUser’s stored credentials
GET/api/integrations/availableConnectable MCP servers + connected?
GET/api/integrations/{mcpServerId}/connect?returnTo={ "authorizeUrl" }
GET/api/integrations/{provider}/callbackOAuth callback → SPA
PUT/api/integrations/{mcpServerId}/tokenStore PAT { "token": "…" }
DELETE/api/integrations/{mcpServerId}/disconnectRevoke

Provider keys include Google and GitHub. Client secrets are never returned in API responses.

Chat credential resolution order

  1. Inline delegation.credential
  2. OAuth / PAT vault for the authenticated user (/api/integrations)
  3. Managed instance via delegation.mcpInstanceId (phone match enforced for WhatsApp federated users)

4.9 Agents & Environments

Configurable agents are project-scoped definitions used by:

  • Interactive chat (agentDefinitionId on /api/ai/chat)
  • Event automations (must name an agent)
  • Headless Kubernetes Jobs (development agents with repository / setup / build / test)
  • Scheduled tasks targeting AgentRun

Deep dive: docs/CONFIGURABLE-AGENTS.md.

Agents — AgentsController (api/agents) — JWT

MethodPathAuthPurpose
GET/api/agents?projectId=Project memberList (+ automation counts)
GET/api/agents/{id}MemberDetail
POST/api/agentsMemberCreate config agent
PUT / DELETE/api/agents/{id}Member*Update / delete (*dev agents with repository: Admin only)
POST/api/agents/development-templateAdminSeed K8s dev agent (setup/build/test)
GET / POST / PUT / DELETE/api/agents/{id}/schedules…AdminLegacy agent cron schedules
POST/api/agents/{id}/runsAdminQueue K8s Job (Agents:Enabled or 503)
GET / POST / DELETE/api/agents/{id}/grants…MemberTool grants (not gated on Automations)

Create config agent (automations / chat):

{
"projectId": 1,
"name": "Support agent",
"description": null,
"providerId": null,
"systemPrompt": "You are a support agent. Read the customer message and draft a reply.",
"temperature": 0.2,
"isActive": true,
"mcpServerIds": [12],
"mcpAccountNames": { "12": "default" }
}

Member-facing CRUD cannot author RepositoryUrl, BaseBranch, setup/build/test commands, or budget fields — those are Admin / development-template concerns.

Start run:

{
"task": "Fix failing tests",
"idempotencyKey": "manual:abc",
"deliveryTarget": "PullRequest",
"gitHubInstallationId": 4287162
}

Tool grant:

{
"integrationConnectionId": "…",
"toolName": "pipedream__slack__send_message",
"externalActionKey": null,
"actionClass": "ExternalCommunication",
"allowedResourcePattern": "#support",
"maxCallsPerRun": 1,
"requiresApproval": true
}

Environments — AgentEnvironmentsController (api/agent-environments) — Admin

MethodPathPurpose
GET / POST/api/agent-environmentsList / create profiles
GET / PUT / DELETE/{id}CRUD
POST/ensure-universalEnsure shared universal profile
GET / POST/prewarmWorkspace image coverage / prewarm

Profiles override image, storage, CPU, memory, and declared egress hosts.


4.10 Agent Runs (headless Jobs)

Controller: AgentRunsController (api/agent-runs) — Admin (Roles=Admin)

Requires Agents:Enabled + Kubernetes for execution. Runs are queued in PostgreSQL, leased, provisioned as Jobs in namespace agent-runs, and streamed via append-only events.

MethodPathPurpose
GET/api/agent-runs?agentId=List (≤200)
GET/{id}Run + artifacts summary
GET/{id}/events?after=SSE resumable event stream
POST/{id}/input{ "input": "…" } when WaitingForInput
POST/{id}/cancelCancel
POST/{id}/retryRetry
GET/{id}/artifactsArtifacts
POST/{id}/memory/{memoryId}/{decision}Memory review
curl -N "https://your-kroov-host/api/agent-runs/42/events?after=0" \
-H "Authorization: Bearer $ADMIN_JWT"

4.11 Interactive Sessions

Controller: AgentSessionsController (api/sessions) — JWT (any authenticated user)
Feature bit: agentSessions = AgentSessions:Enabled && Kubernetes:Enabled

Sessions provision a persistent workspace pod (+ PVC). They can clone a GitHub repository or start empty (general-purpose) when both repositoryFullName and repositoryUrl are omitted. projectId remains required (security, billing, provider, MCP boundary).

MethodPathPurpose
GET / POST/api/sessionsList / create
GET/{id}Detail
GET/{id}/usageLifetime + per-turn usage
POST/{id}/messages{ "message": "…" }
POST/{id}/filesMultipart uploads (files)
POST/{id}/transcribeMultipart audio → text (audio)
PATCH/{id}/settingsprovider/model/mode/budgets
POST/{id}/messages/editEdit + rerun from sequence
POST/{id}/compactManual context compaction
POST/{id}/cancelCancel in-flight turn
POST/{id}/stopStop session
DELETE/{id}Tear down pod + PVC + history
GET/{id}/files?path=Download workspace file (≤25 MB; paths under /workspace/repository or /workspace/uploads)
GET/{id}/exportZIP export (≤50 MB, excludes .git)
GET/{id}/diagnosticsK8s provisioning diagnostics
GET/{id}/diff / /{id}/changesGit diff / changed files
GET/{id}/events?after=SSE event stream

Create:

{
"projectId": 1,
"repositoryFullName": "org/repo",
"repositoryUrl": "https://github.com/org/repo",
"branch": "main",
"gitHubInstallationId": 1,
"environmentProfileKey": "universal",
"providerId": 12,
"model": null,
"title": "Investigate flake",
"firstMessage": "Find why CI fails on main"
}

Omit both repository fields for an empty workspace (still requires projectId). Supplying one repository field requires both.

Settings patch:

{
"providerId": 12,
"model": "gpt-4o",
"mode": "Agent",
"maxInputContextTokens": 128000,
"maxTokens": 500000,
"maxEstimatedCostUsd": 25
}

Modes typically include Agent (read/write tools), Plan, and Ask (read-only tool sets).

Usage semantics: lifetime counters are TotalTurns, ActiveMs, TotalModelCalls, TotalToolCalls, InputTokens, OutputTokens. Per-turn guards ModelCalls / ToolCalls reset each turn — do not use them for lifetime reporting.

MCP tools assigned to the project (with the user’s credential/grant) are resolved automatically for interactive sessions.


4.12 Scheduled Tasks

Controller: ScheduledTasksController (api/scheduled-tasks) — JWT, user-scoped (personal, not admin-global)

Requires ScheduledTasks:Enabled && AgentSessions:Enabled (create/update → 503 otherwise). Feature bit also needs Kubernetes because tasks execute as sessions (or agent runs).

Deep dive: docs/AUTONOMOUS-SCHEDULED-TASKS.md.

MethodPathPurpose
GET / POST/api/scheduled-tasksList / create
GET / PUT / DELETE/{id}CRUD (update re-bases schedule)
POST/{id}/pause | /resume | /run-nowControl
GET/{id}/occurrences?take&skipHistory
GET/occurrences/{occurrenceId}Occurrence + report
POST/occurrences/{occurrenceId}/cancelCancel live run
DELETE/occurrences/{occurrenceId}Delete finished history
GET/preview-schedule?…Dry-run next N slots

Targets:

targetBehaviour
Session (default)Each firing creates a fresh AgentSession with the task’s mode, provider, allow-lists, budgets
AgentRunQueues an AgentRun against an existing AgentDefinition

Recurrence: exactly one of intervalMinutes (anchored on startAtUtc) or cronExpression + timeZoneId (5-field cron).

Create body (selected fields):

{
"name": "Nightly TODO sweep",
"instructions": "Collect TODOs and propose a cleanup plan.",
"projectId": 1,
"description": null,
"isActive": true,
"target": "Session",
"providerId": 12,
"model": null,
"mode": "Plan",
"knowledgeBaseIds": [3],
"mcpServerIds": [12],
"repositoryFullName": "org/repo",
"repositoryUrl": "https://github.com/org/repo",
"branch": "main",
"gitHubInstallationId": 1,
"intervalMinutes": 1440,
"cronExpression": null,
"timeZoneId": "Asia/Jerusalem",
"startAtUtc": "2026-08-09T02:00:00Z",
"endAtUtc": null,
"overlapPolicy": "Skip",
"maxConsecutiveFailures": 3,
"carryOverPreviousReport": true,
"maxTokens": 200000,
"maxEstimatedCostUsd": 5,
"maxToolCallsPerTurn": 40,
"maxModelCallsPerTurn": 40,
"reportEnabled": true
}

Safety rails: overlap Skip/Queue, unique idempotency per slot, auto-pause after consecutive failures, per-occurrence budgets, per-user concurrency caps, workspace teardown after run, DST-aware cron.

Reports: every occurrence ends with ReportMarkdown (narrative + trusted metrics footer), streamed as a report event and stored on the occurrence. Failed/cancelled runs still get a report.

Empty knowledgeBaseIds / mcpServerIds means no restriction (same as interactive sessions).


4.13 Automations, Connections & Approvals

Event automations wake an agent on an external event (Slack message, Shopify order, …) rather than on a schedule or interactive user. They run on an in-process Service runtime and do not require Kubernetes.

Enable with:

export Automations__Enabled=true
export Automations__PublicWebhookBaseUrl=https://your-kroov-host
# Optional Pipedream Connect
export Automations__Pipedream__ProjectId=proj_...
export Automations__Pipedream__ClientId=...
export Automations__Pipedream__ClientSecret=...

When Automations:Enabled=false, automation controllers return 503.
Walkthrough with duplicate-delivery assertions: docs/automations-demo.md.

Concepts

EntityRole
IntegrationConnectionLinked external account + webhook URL + signing secret
AutomationProject-scoped rule: agent + connection + event type + instructions + execution mode
InboundEventSigned webhook delivery (deduped by external delivery id)
InvocationOne agent run for an event (PendingRunningWaitingForApproval / terminal)
ApprovalHuman gate for write/send tool calls
GrantAllow-listed tool with optional resource pattern + approval flag

Execution modes (AutomationExecutionMode):

ModeBehaviour
ObserveRead-only observation of events
DraftAgent may draft; nothing leaves the system
ApproveWritesReads run immediately; mutating/external actions require approval
ScopedAutonomousAllowed granted actions may execute within grant limits without per-action approval
InteractiveReserved for human-watched agent chat invocations (not used by webhook dispatch)

Action classes (AgentActionClass): ReadOnly, ReversibleWrite, ExternalCommunication, Financial, Destructive, CredentialOrPermissionChange.

Runtime kinds (AgentRuntimeKind): Service (in-process automations — default for event automations), Workspace, Workflow.

Pipedream Connect — PipedreamConnectController (api/integrations/pipedream) — JWT

MethodPathPurpose
POST/connect-token{ "projectId" } → Connect token (external user id from principal, never body)
GET/apps?query=Search apps
GET/apps/{appKey}/actionsList actions
POST/connectionsRecord account; webhookSecret returned once
GET/accounts?projectId&appKeyPipedream accounts for user
GET/connectionsLocal IntegrationConnections
POST/connections/{id}/rotate-secretRotate signing secret
DELETE/connections/{id}Revoke
curl -s -X POST https://your-kroov-host/api/integrations/pipedream/connect-token \
-H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
-d '{"projectId":1}'

curl -s -X POST https://your-kroov-host/api/integrations/pipedream/connections \
-H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
-d '{"projectId":1,"appKey":"slack","accountId":"apn_abc123"}'
{
"connection": {
"id": "…",
"webhookUrl": "https://…/api/integration-events/pipedream/…"
},
"webhookSecret": "…"
}

Store webhookSecret immediately — it is encrypted at rest and never returned again (except via rotate).

Webhook gateway — IntegrationEventsController — AllowAnonymous

POST /api/integration-events/{provider}/{connectionId}
  • Connection id is in the path (routing key); signing secret belongs to that connection alone.
  • Pipedream: header x-pd-signature over the raw body.
  • Success: 202 Accepted.
  • Unknown connection or bad signature → identical 401 (no connection-id oracle).
  • Duplicate delivery id → { "duplicate": true, "deliveryId": "…" } with no second side effects.

Automations CRUD — AutomationsController (api/automations) — JWT (project-scoped)

MethodPathPurpose
GET / POST/api/automationsList / create
GET / PUT / DELETE/{id}CRUD
POST/{id}/toggle{ "isActive": true } — new automations start paused
GET/{id}/invocationsInvocation history
GET / POST / DELETE/{id}/grants…Tool grants
{
"projectId": 1,
"agentDefinitionId": 1,
"integrationConnectionId": "<guid>",
"name": "Support inbox",
"eventType": "message",
"instructions": "Read the customer message and draft a reply.",
"runtimeKind": "Service",
"executionMode": "ApproveWrites",
"maxEventsPerHour": 60,
"maxConcurrentRuns": 1
}
{
"toolName": "pipedream__slack__send_message",
"actionClass": "ExternalCommunication",
"allowedResourcePattern": "#support",
"maxCallsPerRun": 1,
"requiresApproval": true
}

An automation must reference an existing agent (POST /api/agents first). Service runtime agents do not need a repository or K8s.

Events inbox — AutomationEventsController (api/automation-events) — JWT

MethodPathPurpose
GET/api/automation-events?projectId&status&takeInbox
GET/dead-letterDead letter
GET/{id}Event + invocations chain
POST/{id}/replayNew attempt (bumps attempt count)
POST/{id}/ignoreIgnore
POST/testDevelopment only — sign a test delivery with the connection’s secret
{
"connectionId": "…",
"eventType": "message",
"payloadJson": "{\"event_id\":\"evt_1\",\"text\":\"where is my order?\"}"
}

/test returns the URL, headers, and body to POST to the real gateway.

Approvals — AutomationApprovalsController (api/automation-approvals) — JWT

MethodPathPurpose
GET/api/automation-approvals?status=List
GET/{id}Detail
POST/{id}/approve{ "comment": "…" } — records decision; ActionWorker executes
POST/{id}/reject{ "comment": "…" }

A second approve returns 409 (exactly-once action execution).


4.14 GitHub App Integration

Controller: GitHubController (api/integrations/github) — JWT (setup callback anonymous)

Used by sessions and agent runs to list installations/repos/branches and clone via the GitHub App.

MethodPathPurpose
GET/statusApp configured?
GET/installationsLinked installations + repos
GET/discoverRelinkable prior installs
POST/linkRelink installation
GET/connectStart GitHub App install
GET/setupAnon callback (installation_id, state)
GET/reposRepos available for agent clone
GET/repos/{owner}/{repo}/branches?installationId=Branches
DELETE/{installationId}/disconnectUnlink

4.15 Cost, Dashboard & Logs

Cost — CostController (api/Cost) — JWT

MethodPathQuery
GET/api/cost/by-request-typeprojectId?, days?
GET/api/cost/by-provideridem
GET/api/cost/by-projectidem
GET/api/cost/by-requestpagination
GET/api/cost/summary

Dashboard — DashboardController (api/Dashboard) — JWT

MethodPathPurpose
GET/api/dashboard/prompt-statsPrompt usage
GET/api/dashboard/token-usageTokens
GET/api/dashboard/provider-statsPer provider
GET/api/dashboard/summaryOverview
GET/api/dashboard/logsLog list
GET/api/dashboard/logs/{id}Log detail

API-key scoped logs also available at GET /api/ai/logs.


4.16 Security & Health

Security — SecurityController (api/security)

MethodPathAuthPurpose
GET/api/security/statusAdminOnlySecurity mode, audit, CSP, payload writer status
POST/api/security/csp-reportAnonymousBrowser CSP violation reports (204)

Health

curl -s https://your-kroov-host/health
# { "status": "healthy" }

5. Request & Response Formats

5.1 Standard Request Format

API Key (AI / OpenAI-compat):

x-api-key: your-api-key-here
Content-Type: application/json

JWT (SPA / admin / agents):

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json

Body (JSON, camelCase):

{
"prompt": "Your query here",
"projectId": 1,
"providerId": 12,
"stream": false
}

5.2 Standard AI Response Format

Success:

{
"success": true,
"content": "AI response text here",
"errorMessage": null,
"provider": "Azure",
"providerId": 12,
"executionDuration": "00:00:02.345",
"requestId": 12345,
"inputTokens": 25,
"outputTokens": 150
}

Validation / problem details may also use ASP.NET Core’s default error shapes (title, errors, traceId) on admin APIs.

5.3 Response Fields (AI)

FieldTypeDescription
successbooleanOutcome
contentstringModel text (null on error)
errorMessagestringError description
providerstringProvider type used
providerIdintegerInstance id
executionDurationstringHH:mm:ss.fff
requestIdintegerFor logging / voting
inputTokens / outputTokensintegerToken counts
audioDurationnumberSeconds (audio)
chatIdGUID stringChat session
messageIndexintegerTurn index
sessionEndedbooleanChat closed

5.4 File Upload Format

Content-Type: multipart/form-data. Field names are camelCase (providerId, projectId, noLog, files, audio, …).

curl -X POST https://your-kroov-host/api/ai/queryImage \
-H "x-api-key: your-api-key" \
-F "file=@image.png" \
-F "prompt=Analyze this image" \
-F "projectId=1" \
-F "providerId=12"

6. Streaming API

6.1 Server-Sent Events (SSE)

Used by:

SurfaceEndpointAuth
AI chat / query (stream: true)POST /api/ai/chat or /queryAPI Key
Agent runsGET /api/agent-runs/{id}/events?after=Admin JWT
Interactive sessionsGET /api/sessions/{id}/events?after=JWT

Headers (client):

Accept: text/event-stream

Typical AI chat events:

Event / dataMeaning
contentText delta
toolCallsTool invocations / results
statusRound progress
tokensCumulative token counts
errorTerminal error
[DONE]Stream end

Resumable streams (runs / sessions): pass after=<lastEventId> to continue after reconnect.

6.2 Client-Side Handling (AI SSE)

const response = await fetch('https://your-kroov-host/api/ai/chat', {
method: 'POST',
headers: {
'x-api-key': apiKey,
'Content-Type': 'application/json',
'Accept': 'text/event-stream'
},
body: JSON.stringify({
messageUser: 'Summarize the key points',
projectId: 1,
providerId: 12,
stream: true
})
});

const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';

while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.startsWith('data:')) continue;
const data = line.slice(5).trim();
if (data === '[DONE]') return;
try {
const evt = JSON.parse(data);
if (evt.content) process.stdout?.write?.(evt.content);
} catch { /* ignore keep-alives */ }
}
}

6.3 WebSocket realtime STT

See WebSocket — Live realtime transcription under §4.1.


7. Error Handling

7.1 HTTP Status Codes

CodeMeaningTypical cause
200OKSuccess
202AcceptedWebhook accepted for async processing
204No ContentCSP report stored
400Bad RequestValidation / malformed body
401UnauthorizedMissing/invalid API key, JWT, webhook signature, unknown connection
403ForbiddenProject access denied; non-admin on AdminOnly route
404Not FoundUnknown id
409ConflictDuplicate name; second approval; optimistic conflict
413Payload Too LargeUpload over limit
429Too Many RequestsIf rate limiting is enabled in a given deployment
500Internal Server ErrorUnexpected failure
502Bad GatewayUpstream AI provider failure
503Service UnavailableFeature disabled / KB not supported / hosted MCP unavailable

7.2 Feature-gate 503 examples

{ "error": "Event automations are disabled on this deployment." }
{ "message": "Internal knowledge bases require PostgreSQL with pgvector." }

Call GET /api/features before enabling UI affordances.

7.3 Retry Strategy

StatusRetry?Guidance
408 / 429 / 502 / 503YesExponential backoff with jitter
500CautiousRetry idempotent GETs; use idempotency keys on creates where supported
400 / 401 / 403 / 404 / 409NoFix request / credentials

Idempotency:

  • MCP instance create: optional Idempotency-Key header
  • Agent runs: idempotencyKey in body
  • Scheduled task occurrences: unique (ScheduledTaskId, IdempotencyKey) per slot
  • Automation webhooks: dedupe on external delivery id

7.4 Error Logging

Always capture: HTTP status, requestId (when present), correlation / trace id, timestamp, endpoint, project id. Never log API keys, JWTs, webhook secrets, or raw sensitive data beyond what your compliance policy allows.


8. Code Examples

8.1 JavaScript / TypeScript — reusable AI client

type AiQueryOptions = {
promptId?: number;
provider?: string;
providerId?: number;
stream?: boolean;
externalId?: string;
externalUser?: string;
noLog?: boolean;
knowledgeBaseIds?: number[];
agentDefinitionId?: number;
};

class KroovClient {
constructor(
private readonly baseUrl: string,
private readonly apiKey: string
) {}

async query(prompt: string, projectId: number, options: AiQueryOptions = {}) {
const response = await fetch(`${this.baseUrl}/api/ai/query`, {
method: 'POST',
headers: {
'x-api-key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({ prompt, projectId, stream: false, ...options })
});
if (!response.ok) {
const err = await response.json().catch(() => ({}));
throw new Error(err.errorMessage || `HTTP ${response.status}`);
}
return response.json();
}

async chat(
messageUser: string,
projectId: number,
options: AiQueryOptions & { chatId?: string | null; delegation?: object } = {}
) {
const response = await fetch(`${this.baseUrl}/api/ai/chat`, {
method: 'POST',
headers: {
'x-api-key': this.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({ messageUser, projectId, stream: false, ...options })
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
return response.json();
}

async *streamChat(messageUser: string, projectId: number, options: Record<string, unknown> = {}) {
const response = await fetch(`${this.baseUrl}/api/ai/chat`, {
method: 'POST',
headers: {
'x-api-key': this.apiKey,
'Content-Type': 'application/json',
Accept: 'text/event-stream'
},
body: JSON.stringify({ messageUser, projectId, stream: true, ...options })
});
if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`);

const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (!line.startsWith('data:')) continue;
const data = line.slice(5).trim();
if (data === '[DONE]') return;
try { yield JSON.parse(data); } catch { /* keep-alive */ }
}
}
}
}

// Usage
const client = new KroovClient('https://your-kroov-host', process.env.AINEXUS_API_KEY!);
const result = await client.query('Summarize the key points', 1, { providerId: 12 });
console.log(result.content);

8.2 Python — chat with knowledge bases + streaming

import json
import requests

BASE = "https://your-kroov-host"

def chat(api_key: str, message: str, project_id: int, kb_ids: list[int], chat_id: str | None = None):
r = requests.post(
f"{BASE}/api/ai/chat",
headers={"x-api-key": api_key, "Content-Type": "application/json"},
json={
"messageUser": message,
"projectId": project_id,
"chatId": chat_id,
"knowledgeBaseIds": kb_ids,
"stream": False,
},
timeout=120,
)
r.raise_for_status()
return r.json()

def stream_query(api_key: str, prompt: str, project_id: int, provider_id: int | None = None):
with requests.post(
f"{BASE}/api/ai/query",
headers={"x-api-key": api_key, "Content-Type": "application/json"},
json={"prompt": prompt, "projectId": project_id, "providerId": provider_id, "stream": True},
stream=True,
timeout=300,
) as response:
response.raise_for_status()
for line in response.iter_lines(decode_unicode=True):
if not line or not line.startswith("data:"):
continue
data = line[5:].strip()
if data == "[DONE]":
break
try:
evt = json.loads(data)
except json.JSONDecodeError:
continue
if content := evt.get("content"):
print(content, end="", flush=True)

8.3 C# — OpenAI-compatible client

using var http = new HttpClient { BaseAddress = new Uri("https://your-kroov-host") };
http.DefaultRequestHeaders.Add("Authorization", $"Bearer {apiKey}");

var payload = new {
model = "gpt-4o",
messages = new[] { new { role = "user", content = "Summarize the key guidance" } },
extension = new { noLog = false }
};

var res = await http.PostAsJsonAsync("/v1/chat/completions", payload);
res.EnsureSuccessStatusCode();
var json = await res.Content.ReadAsStringAsync();

8.4 cURL — automation happy path (dev)

TOKEN=$(curl -s -X POST https://localhost:7003/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin"}' | jq -r .token)
AUTH="Authorization: Bearer $TOKEN"

# 1) Agent
AGENT_ID=$(curl -s -X POST https://localhost:7003/api/agents \
-H "$AUTH" -H 'Content-Type: application/json' \
-d '{"projectId":1,"name":"Support agent","systemPrompt":"Draft replies."}' | jq -r .id)

# 2) After Pipedream Connect UI records a connection:
# curl -s -X POST https://localhost:7003/api/automations -H "$AUTH" -H 'Content-Type: application/json' -d '{...}'
# curl -s -X POST https://localhost:7003/api/automations/$ID/grants -H "$AUTH" ...
# curl -s -X POST https://localhost:7003/api/automations/$ID/toggle -H "$AUTH" -d '{"isActive":true}'
# curl -s -X POST https://localhost:7003/api/automation-events/test -H "$AUTH" -d '{...}'
# Full script: docs/automations-demo.md

8.5 cURL — create interactive session (empty workspace)

curl -s -X POST https://localhost:7003/api/sessions \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"projectId": 1,
"providerId": 12,
"title": "Scratch workspace",
"firstMessage": "Create a short project plan markdown file"
}'

8.6 cURL — knowledge base upload + chat grounding

# Admin JWT required for KB admin APIs
curl -s -X POST https://localhost:7003/api/knowledge-bases \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{
"projectId": 1,
"name": "Protocols",
"embeddingProviderId": 3,
"embeddingModel": "text-embedding-3-small",
"chunkSize": 1000,
"chunkOverlap": 100,
"isActive": true
}'

curl -s -X POST https://localhost:7003/api/knowledge-bases/1/documents \
-H "Authorization: Bearer $TOKEN" \
-F "files=@protocol.pdf"

# Inference uses API key + knowledgeBaseIds (after documents reach Indexed)
curl -s -X POST https://localhost:7003/api/ai/chat \
-H "x-api-key: $API_KEY" -H 'Content-Type: application/json' \
-d '{
"messageUser": "What does the protocol say about escalation?",
"projectId": 1,
"providerId": 12,
"knowledgeBaseIds": [1]
}'

9. Best Practices

9.1 API Key & JWT Management

  • Prefer API keys for service-to-service AI calls; JWT for user-context admin/agent operations
  • Scope keys to a project when possible
  • Rotate keys; revoke immediately on compromise
  • Use refresh tokens; never embed long-lived JWTs in mobile binaries without a refresh flow

9.2 Provider Routing

  • Prefer providerId for deterministic routing and cost attribution
  • Check capability flags (text/image/audio/embeddings/agentTools/realtime) before calling
  • Keep a fallback provider strategy client-side for 502 responses

9.3 Chat & MCP

  • Use stream: true for tool-using chats
  • Constrain tools with project MCP assignments + per-user credentials + grants
  • For multi-MCP, set delegation.mcpServerIds explicitly; use [] only when you intentionally want grants-only tools
  • Never put long-lived vendor secrets in client-side code — use the vault (/api/integrations) or managed instances

9.4 Knowledge Bases

  • Chunk size / overlap defaults are fine for most corpora; reindex after embedding model changes
  • Share bases across projects via PUT .../projects instead of duplicating uploads
  • Use knowledgeBaseIds on chat for ad-hoc grounding without a dedicated KB provider

9.5 Agents, Sessions & Tasks

  • Start autonomous work in Plan or Ask mode before enabling write tools
  • Set budgets (maxTokens, maxEstimatedCostUsd, tool/model call caps) on sessions and scheduled tasks
  • Prefer overlapPolicy: Skip for scheduled tasks unless concurrent work is intentional
  • Export session artifacts (/export) before DELETE if you need the files

9.6 Automations

  • Create automations paused, add grants, then toggle active
  • Prefer ApproveWrites for anything that sends external messages
  • Treat webhook secrets like passwords; rotate with /rotate-secret
  • Rely on platform dedupe — still design handlers to be safe under at-least-once delivery

9.7 Performance & Resilience

  • Reuse chatId instead of resending full history via messageHistory when possible
  • Use resumable SSE (after=) for long agent runs
  • Apply exponential backoff on 502/503
  • Watch cost endpoints in production integrations

10. Security Considerations

10.1 Input Validation

  • Enforce max prompt / attachment sizes client-side as well as server-side
  • Sanitize filenames on uploads
  • Treat model output as untrusted when displaying in HTML (XSS)

10.2 Data Minimization & Sensitive Data

  • Send the minimum sensitive data required for the task
  • Prefer externalId opaque tokens over raw personal identifiers when feasible
  • Use noLog only when the project explicitly allows it — metrics still remain
  • Follow your organisation's Acceptable Use and retention policies

10.3 Secrets

SecretStorage
API keysSecrets manager / env
JWT / refreshHttpOnly secure storage / memory
Pipedream webhookSecretSecrets manager (shown once)
MCP OAuth client secretsServer-side only (never in API responses)
Jwt:SecretKey / Encryption:MasterKeyEnvironment / K8s Secret — never committed

10.4 Webhook authenticity

  • Verify signatures with the connection’s secret
  • Reject unsigned bodies
  • Do not distinguish “unknown connection” from “bad signature” in client logs exposed to untrusted parties

10.5 Network

  • Production APIs are often internal-only
  • Public webhook base URLs (automations) must be HTTPS in production and tightly scoped
  • Hosted MCP / agent pods run with restricted Pod Security; do not inject host credentials into workspaces

11. Troubleshooting

11.1 Common Issues

401 Unauthorized

  • Missing/invalid x-api-key or JWT
  • Expired API key or access token — refresh or renew
  • Webhook: wrong signature or connection id
  • /api/ai/* called with JWT only — use an API key

403 Forbidden

  • API key project mismatch
  • Non-admin calling AdminOnly route
  • Member trying to edit a development agent (repository-backed)

400 Bad Request

  • Missing prompt / messageUser / multipart file
  • Invalid cron / time zone on scheduled tasks
  • KB / MCP ids not visible to the project

409 Conflict

  • Duplicate knowledge-base name in project
  • Second automation approval
  • Unique constraint (idempotency)

502 Bad Gateway

  • Upstream model provider outage or quota
  • Retry with backoff; fail over providerId

503 Service Unavailable

  • Feature flag off — check GET /api/features
  • Internal KB on non-PostgreSQL
  • Hosted MCP / agents without Kubernetes
  • Automations disabled

Slow responses / stuck sessions

  • Check /api/sessions/{id}/diagnostics for pod provisioning
  • Confirm kubeconfig / cluster capacity
  • Review budgets and tool-loop limits

11.2 Debugging Tips

# Capability matrix
curl -s https://localhost:7003/api/features | jq

# Auth modes
curl -s https://localhost:7003/api/auth/options | jq

# Health
curl -s https://localhost:7003/health

# AI smoke (API key)
curl -s -X POST https://localhost:7003/api/ai/query \
-H "x-api-key: $API_KEY" -H 'Content-Type: application/json' \
-d '{"prompt":"ping","projectId":1}'

# Swagger (Development)
open https://localhost:7003/swagger

Correlate failures with Dashboard logs (requestId) and, for agents/sessions, SSE event streams.

11.3 Getting Help

Include: environment (prod/dev/local), endpoint, HTTP status, requestId, project id, feature-flag snapshot (/api/features), redacted request body, and timestamp. Contact your platform administrator or operations team.


Appendices

Appendix A: Complete API Reference (summary)

Auth legend: K = API Key, J = JWT, A = Admin, Anon = AllowAnonymous, D = Delegation, HMAC = webhook signature, T = MCP controller token.

MethodEndpointAuthPurpose
GET/healthAnonLiveness
GET/api/featuresAnonCapability flags
GET/api/auth/optionsAnonLogin modes
POST/api/auth/loginAnonLocal login
GET/api/auth/googleAnonGoogle OAuth start
POST/api/auth/refreshAnonRefresh JWT
POST/api/auth/logoutAnonRevoke refresh
POST/api/auth/apikeyJCreate API key
GET/api/auth/apikeyJList API keys
PUT/api/auth/changepasswordJChange password
POST/api/ai/queryKText query
POST/api/ai/queryImageKVision
POST/api/ai/queryDocumentKDocument Q&A
POST/api/ai/queryAudioKBatch STT
WS/api/ai/realtime/transcribeKLive STT
POST/api/ai/chatKChat + MCP/KB
POST/api/ai/vote/{requestId}KFeedback
POST/api/ai/vote/chat/{chatId}/{messageIndex}KChat feedback
GET/api/ai/logsKProject logs
GET/api/ai/assistantsKAzure assistants
GET/api/ai/download-blob / /view-blobKBlob access
GET/POST/v1/models, /v1/chat/completions, /v1/audio/transcriptions, /v1/embeddingsKOpenAI compat
GET/api/chat/sessions, /api/chat/conversation/{id}J/KChat history
GET/api/chatadmin/…AAdmin chat history
CRUD/api/project…J/AProjects & members
*/api/projects/{id}/federation/…AFederation
POST/api/delegations/exchangeKDelegation token
CRUD/api/prompt…J/APrompts
CRUD/api/provider…J/AProviders
CRUD/api/knowledge-bases…AInternal KB admin
GET/api/projects/{id}/knowledge-basesJKB picker
CRUD/api/mcp-servers…J/AMCP servers
*/api/mcp-catalog…ACatalog
*/api/mcp-servers/{id}/definition…A/JDefinitions & creds
*/api/mcp-instances…J/DManaged instances
*/api/mcp-runtime…ARuntime profiles
*/api/mcp-runtime-proxy/{id}/mcp/TData plane
*/api/integrations…JUser OAuth/PAT
CRUD/api/agents…J/AAgents & grants
CRUD/api/agent-environments…AEnvironments
*/api/agent-runs…AHeadless runs + SSE
*/api/sessions…JInteractive sessions + SSE
*/api/scheduled-tasks…JAutonomous tasks
*/api/automations…JAutomations & grants
*/api/automation-events…JEvent inbox / test
*/api/automation-approvals…JApprovals
POST/api/integration-events/{provider}/{connectionId}HMACWebhook gateway
*/api/integrations/pipedream…JPipedream Connect
*/api/integrations/github…JGitHub App
GET/api/cost/…JCost analytics
GET/api/dashboard/…JDashboard
GET/api/security/statusASecurity status
POST/api/security/csp-reportAnonCSP reports

Appendix B: Rate Limits

Default: no global platform rate limit is guaranteed in every deployment. Automations expose maxEventsPerHour / maxConcurrentRuns per automation. Clients should still implement polite client-side throttling and backoff on 429/503.

Appendix C: SDK Libraries

LanguageSuggestion
C#Official Kroov.Sdk NuGet (v3.0+) — AI, MCP, agents, sessions, automations, Pipedream; see src/Kroov.Sdk/README.md
JavaScript/TypeScriptfetch / axios
Pythonrequests / httpx
Any OpenAI SDKPoint base URL at {host}/v1 with API key as Bearer

Appendix D: Changelog

VersionDateChanges
2.0.1Aug 2026Restored as the standalone integrator contract (docs/API-INTEGRATION-GUIDE.md and /docs/api/integration-guide) after the docs cleanup
2.0Aug 2026Full refresh: feature flags; ApiKey-only /api/ai/*; providerId on image/document; OpenAI /v1; Knowledge Bases; MCP definitions/diagnostics/proxy; Agents, presets, environments, runs; interactive sessions; scheduled tasks; automations / Pipedream / approvals / webhooks; GitHub App; federation; security/AD; corrected vote field isThumbsUp; expanded appendix
1.1May 2026Live realtime transcription WebSocket; optional language / auto-detect for batch and live audio
1.0Jan 2026Initial API release (AI query/chat + early MCP section)

Document Classification: Public - Developer Documentation
Prepared by: Kroov platform team
Last Updated: August 2026
API Version: 2.0 (platform surface)


End of API Integration Guide