Examples, practices, troubleshooting
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 walkthrough available in the automations guides
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]
}'
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
providerIdfor deterministic routing and cost attribution - Check capability flags (text/image/audio/embeddings/agentTools/realtime) before calling
- Keep a fallback provider strategy client-side for
502responses
9.3 Chat & MCP
- Use
stream: truefor tool-using chats - Constrain tools with project MCP assignments + per-user credentials + grants
- For multi-MCP, set
delegation.mcpServerIdsexplicitly; 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 .../projectsinstead of duplicating uploads - Use
knowledgeBaseIdson chat for ad-hoc grounding without a dedicated KB provider
9.5 Agents, Sessions & Tasks
- Start autonomous work in
PlanorAskmode before enabling write tools - Set budgets (
maxTokens,maxEstimatedCostUsd, tool/model call caps) on sessions and scheduled tasks - Prefer
overlapPolicy: Skipfor scheduled tasks unless concurrent work is intentional - Export session artifacts (
/export) beforeDELETEif you need the files
9.6 Automations
- Create automations paused, add grants, then
toggleactive - Prefer
ApproveWritesfor 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
chatIdinstead of resending full history viamessageHistorywhen possible - Use resumable SSE (
after=) for long agent runs - Apply exponential backoff on
502/503 - Watch cost endpoints in production integrations
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
externalIdopaque tokens over raw personal identifiers when feasible - Use
noLogonly when the project explicitly allows it — metrics still remain - Follow your organisation's Acceptable Use and retention policies
10.3 Secrets
| Secret | Storage |
|---|---|
| API keys | Secrets manager / env |
| JWT / refresh | HttpOnly secure storage / memory |
Pipedream webhookSecret | Secrets manager (shown once) |
| MCP OAuth client secrets | Server-side only (never in API responses) |
Jwt:SecretKey / Encryption:MasterKey | Environment / 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
Troubleshooting
11.1 Common Issues
401 Unauthorized
- Missing/invalid
x-api-keyor 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/ multipartfile - 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}/diagnosticsfor 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.