Documentation Index
Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
Use this file to discover all available pages before exploring further.
POST /api/v1/ai/chat
Stream a conversation with the AI assistant. Uses the Vercel AI SDK streamText() to stream responses with multi-step tool calling support (up to 5 tool steps per message). Messages are persisted to the database after the stream completes. Conversations are auto-created if no conversationId is provided.
Rate limited to 30 requests per 60 seconds per user. Request timeout: 60 seconds.
| Header | Required | Description |
|---|
Content-Type | Yes | application/json |
X-Tenant-Id | Yes | Tenant identifier |
X-User-Id | No | User identifier for conversation ownership |
Request Body
| Field | Type | Required | Description |
|---|
messages | array | Yes | AI SDK message array. Each message has role (user, assistant, tool) and content (string or parts array). Must not be empty. |
route | string | No | Current page route for context routing (e.g., "/studio/decision-flows"). Determines which tools and system prompt are used. Defaults to "/" (general context with all tools). |
conversationId | string | No | Existing conversation ID to continue. If omitted, a new conversation is auto-created. When provided, persisted message history is prepended to the messages array. |
Response
Returns a streaming UIMessageStreamResponse (Server-Sent Events). The response includes:
X-Conversation-Id header with the conversation ID for follow-up messages
- Streamed text chunks and tool call results in AI SDK wire format
Roles
admin, editor, viewer
Example
curl -N -X POST https://playground.kaireonai.com/api/v1/ai/chat \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: my-tenant" \
-d '{
"messages": [{ "role": "user", "content": "What are my top performing offers?" }],
"route": "/dashboards"
}'
Context Routing
The route parameter determines which tools are available:
| Route Pattern | Module | Tools Available |
|---|
/data/schemas | Data | 18 read-only + addSchemaField, getSchemaFields, createSchema |
/data/flow-pipelines | Data | 18 read-only + getSchemaFields, createFlowPipeline, updateFlowPipeline |
/data/* | Data | 18 read-only + createConnector, testConnector |
/studio/decision-flows | Studio | 18 read-only + 30+ creation, mutation, V2 pipeline, and intelligence tools |
/studio/treatments | Studio | 18 read-only + creation tools + content generation + 8 CMS tools |
/algorithms/* | Algorithms | 18 read-only + model management + 4 model intelligence tools |
/dashboards/* | Dashboards | 18 read-only + runHealthCheck, analyzeOfferPerformance, analyzePolicyConflicts |
| Any other | General | All 105+ tools |
GET /api/v1/ai/conversations
List recent conversations for the tenant (up to 50, newest first). Requires session authentication.
Response
[
{
"id": "conv_abc123",
"title": "What are my top performing offers?",
"userId": "user_001",
"createdAt": "2026-03-18T10:30:00Z",
"updatedAt": "2026-03-18T10:35:00Z",
"user": { "name": "Admin User", "image": null }
}
]
Roles
Requires session authentication (any authenticated user).
POST /api/v1/ai/conversations
Create a new conversation.
Request Body
| Field | Type | Required | Description |
|---|
title | string | No | Conversation title. Default: "New conversation" |
Response: 201 Created with the created conversation object.
Roles
Requires session authentication.
GET /api/v1/ai/conversations/
Get a single conversation with its full message history, ordered by creation time ascending.
Path Parameters
| Parameter | Type | Description |
|---|
id | string | Conversation ID |
Response
{
"id": "conv_abc123",
"title": "What are my top performing offers?",
"tenantId": "my-tenant",
"userId": "user_001",
"createdAt": "2026-03-18T10:30:00Z",
"updatedAt": "2026-03-18T10:35:00Z",
"messages": [
{ "role": "user", "content": "What are my top performing offers?", "createdAt": "2026-03-18T10:30:00Z" },
{ "role": "assistant", "content": "Based on the data...", "toolInvocations": [...], "createdAt": "2026-03-18T10:30:05Z" }
]
}
Roles
Requires session authentication. Only conversations belonging to the user’s tenant are accessible.
DELETE /api/v1/ai/conversations/
Delete a conversation and all its messages. Only deletes conversations belonging to the authenticated user’s tenant.
Path Parameters
| Parameter | Type | Description |
|---|
id | string | Conversation ID |
Response
Roles
admin, editor
POST /api/v1/ai/analyze/
Run AI-powered analysis on tenant data. Supports three analysis types with automatic LLM/ML Worker routing based on data volume. The ML row threshold is 5,000 rows.
Path Parameters
| Parameter | Values | Description |
|---|
type | "policies", "segments", "content" | Type of analysis to perform |
Request Body
| Field | Type | Required | Description |
|---|
schemaId | string | Required for segments | ID of the data schema to analyze |
confirmed | boolean | No | Set true to bypass the large-dataset confirmation dialog |
preferredTier | "llm" or "ml_worker" | No | Override automatic tier routing |
Analysis Types
| Type | Data Source | LLM Method | ML Worker Method |
|---|
segments | DataSchema rows and field statistics | generateObject() with field summaries to propose segment definitions | K-Means clustering with silhouette scoring on full dataset |
policies | InteractionHistory table | generateObject() with channel breakdown and frequency bands | Statistical frequency binning and correlation analysis |
content | Treatment (Creative) records (max 200) | generateObject() with creative metadata and performance metrics | TF-IDF and statistical content pattern analysis |
Response — Confirmation Required
When the dataset exceeds 5,000 rows and confirmed is not true:
{
"requiresConfirmation": true,
"tier": "ml_worker",
"warning": "Dataset has 50,000 rows. For datasets over 5,000 rows, enable the ML Worker for faster and cheaper analysis.",
"confirmationMeta": {
"rowCount": 50000,
"accuracy": "ML Worker uses K-Means clustering, logistic regression, and TF-IDF -- more accurate than LLM pattern matching for large datasets.",
"costEstimate": "Proceeding with LLM will use approximately 3,750,500 tokens (~$0.56).",
"speedComparison": "ML Worker processes locally in seconds vs LLM round-trip latency."
}
}
Response — Analysis Complete
{
"results": [
{ "name": "High-Value Loyalists", "description": "...", "size": 2340, "percentage": 18, "filterRules": [...], "characteristics": [...], "suggestedUse": "..." }
],
"source": "llm",
"analysisType": "segments"
}
Response — ML Worker Job Submitted
{
"jobId": "job_abc123",
"source": "ml_worker",
"analysisType": "segments"
}
Heuristic Fallbacks
Each analyzer falls back to deterministic heuristics if the LLM call fails:
- Segments: Splits the most variable numeric field into Low/Mid/High at the 33rd and 67th percentiles
- Policies: Finds the frequency band with the highest conversion rate and recommends a cap one step below the drop-off point
- Content: Computes CTR/CVR per creative, flags underperformers below 50% of average, highlights top performers above 150% of average
Roles
admin, editor
GET /api/v1/ai/analyzer-settings
Get current AI analyzer settings for the tenant with defaults filled in. Returns four sections: segmentation, policy, content, and ruleBuilder.
Response
{
"segmentation": {
"minClusters": 2,
"maxClusters": 8,
"algorithm": "kmeans",
"includedFeatures": null
},
"policy": {
"dailyCap": 3,
"weeklyCap": 10,
"monthlyCap": 30,
"lookbackDays": 90,
"minSampleSize": 100
},
"content": {
"minImpressions": 100,
"metricWeights": { "ctr": 0.33, "cvr": 0.34, "revenue": 0.33 },
"confidenceLevel": 0.95
},
"ruleBuilder": {
"maxConditions": 5,
"allowedOperators": ["equals", "gt", "lt", "gte", "lte", "contains", "in"],
"fieldTypeConstraints": null
}
}
Roles
admin, editor, viewer
PUT /api/v1/ai/analyzer-settings
Update AI analyzer settings. Each sub-schema is validated independently using Zod. Invalid values in one section do not affect valid values in other sections.
Request Body
Partial object matching the response structure of GET /api/v1/ai/analyzer-settings. Only include sections you want to update.
{
"segmentation": { "maxClusters": 12 },
"policy": { "lookbackDays": 180 }
}
Response
Returns the full merged settings object (same format as GET).
Roles
admin only
POST /api/v1/ai/intelligence
Dispatch to intelligence backend tools. This is a bridge endpoint used by the MCP server (which runs as a separate stdio process and cannot import Next.js server modules directly).
Request Body
| Field | Type | Required | Description |
|---|
tool | string | Yes | Tool name (see table below) |
params | object | Yes | Tool-specific parameters. tenantId is auto-injected. |
| Tool | Parameters | Description |
|---|
explainDecision | customerId, offerId?, decisionFlowKey? | Explain why a customer received or did not receive an offer. Full funnel walkthrough. |
compareOfferEligibility | customerId, offerIds (2-5) | Side-by-side offer eligibility comparison. |
listCustomerSuppressions | customerId | Active contact policy suppressions for a customer. |
traceCustomerJourney | customerId, limit? (default 20) | Customer activity timeline: interactions, journeys, experiments. |
analyzeQualificationFunnel | decisionFlowKey? | Funnel analysis identifying bottleneck rules. |
analyzeContactPolicySuppression | channel?, period? (day/week/month) | Suppression rate analysis by rule type and channel. |
analyzePolicyConflicts | (none) | Detect conflicts across offers, rules, policies, experiments. |
analyzeOfferPerformance | period? (day/week/month), limit? (default 20) | Offer performance with trends and actionable insights. |
simulateRuleChange | ruleId, proposedChange: { field, oldValue, newValue } | Before/after reach estimation for rule changes. |
simulateFrequencyCapChange | channel, currentCap, newCap, period | Impact of frequency cap change: customers unlocked/suppressed, fatigue risk. |
analyzeModelHealth | modelId | Model metrics, performance trend, feature importance, data freshness, health verdict. |
explainModelScoring | modelId, customerId | Score breakdown: raw score, percentile, contributing features with direction. |
suggestModelImprovements | modelId | Missing predictors, model type advice, unused features, hyperparameter suggestions. |
detectModelDrift | modelId | Distribution shift detection, calibration check, action recommendation. |
runHealthCheck | (none) | Comprehensive tenant health check (cached 5 minutes). |
analyzeCrossModule | (none) | Cross-module correlation analysis (see below). |
analyzeCrossModule Response
Returns insights that connect dots across modules — the platform’s most valuable intelligence layer.
{
"tenantId": "tenant_001",
"analyzedAt": "2026-03-26T15:34:24.991Z",
"insights": [
{
"id": "policy-blocks-offer_001",
"type": "policy_blocks_top_offer",
"severity": "warning",
"title": "Category suppression may limit \"Win-Back Lapsed Policy\"",
"description": "\"Win-Back Lapsed Policy\" has a 9.8% CVR (top performer) but category suppression may be blocking repeat impressions.",
"impact": "Potential revenue loss: relaxing this policy for high-value customers could increase conversions by 10-20%.",
"action": {
"label": "Review Policy",
"type": "navigate",
"href": "/studio/contact-policies"
},
"entities": [
{ "type": "offer", "id": "offer_001", "name": "Win-Back Lapsed Policy" },
{ "type": "contactPolicy", "id": "cp_001", "name": "Category Suppression After Dismiss" }
]
}
],
"summary": { "total": 4, "critical": 0, "warning": 4 }
}
| Correlation Type | Severity | What It Detects |
|---|
policy_blocks_top_offer | warning | Contact policy suppressing a high-CVR offer |
low_auc_model_in_active_flow | critical | Near-random model active while decision flow is published |
zero_cvr_high_spend | warning | Offer with many impressions but zero conversions |
Each insight includes an action object that the UI renders as a one-click button (either navigation or direct API call).
Response
Returns the tool’s result directly as JSON. Errors return 500 with { "error": { "message": "..." } }.
Roles
admin, editor, viewer
POST /api/v1/ai/parse-rule
Parse a natural language rule description into structured entity definitions. Uses LLM-powered parsing with regex-based heuristic fallback if the LLM call fails.
The parser loads the tenant’s data schemas to provide field context to the LLM. It produces three entity types: qualification_rule, contact_policy, and behavioral_metric.
Request Body
| Field | Type | Required | Description |
|---|
text | string | Yes | Natural language rule description |
Response
{
"entities": [
{
"type": "qualification_rule",
"name": "Age >= 25",
"payload": {
"conditions": [{ "field": "customer.age", "operator": "gte", "value": 25 }],
"ruleType": "hard"
}
},
{
"type": "behavioral_metric",
"name": "total_spend_90d",
"payload": {
"aggregation": "sum",
"field": "revenue",
"windowDays": 90,
"description": "Sum of revenue from conversions over the last 90 days"
}
},
{
"type": "qualification_rule",
"name": "Spend >= $500 (90d)",
"payload": {
"conditions": [{ "field": "total_spend_90d", "operator": "gte", "value": 500 }],
"ruleType": "hard"
}
}
],
"warnings": [
"No 'age' field found in available schemas. The rule may not work until the field is added."
]
}
Heuristic Fallback Patterns
When the LLM is unavailable, the parser handles these regex patterns:
| Pattern | Example Input | Parsed Entity |
|---|
| Age comparison | ”over 25”, “aged 18 or older” | qualification_rule with customer.age >= N |
| Spend threshold | ”spent more than $500” | qualification_rule with customer.total_spend >= N |
| Time-windowed spend | ”spent $500 in the last 90 days” | behavioral_metric (sum revenue, 90d) + qualification_rule |
| Negation | ”haven’t converted on credit card” | contact_policy with outcome-based suppression |
| Category detection | ”credit card offers”, “loan offers” | Scope applied to contact policy |
Example
curl -X POST https://playground.kaireonai.com/api/v1/ai/parse-rule \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: my-tenant" \
-d '{ "text": "Only show to customers over 25 who spent more than $500 in the last 90 days and haven'\''t converted on credit card offers" }'
Roles
admin, editor
GET /api/v1/ai/recommendations
List AI-generated recommendations for the tenant, ordered by creation date descending.
Query Parameters
| Parameter | Type | Values | Description |
|---|
type | string | policy, rule, segment, content | Filter by recommendation type |
status | string | new, reviewed, applied, dismissed | Filter by status |
Response
{
"recommendations": [
{
"id": "rec_001",
"tenantId": "my-tenant",
"type": "policy",
"title": "Set frequency cap to 3/week",
"description": "Analysis of 12,500 interactions shows conversion rate peaks at 3 contacts/week (8.2%). Rate drops 65% at 7 contacts/week.",
"payload": {
"ruleType": "frequency_cap",
"scope": "global",
"maxContactsPerWeek": 3
},
"confidence": "high",
"source": "llm",
"status": "new",
"metadata": {
"frequencyBands": [
{ "contactsPerWeek": 1, "conversionRate": 0.05 },
{ "contactsPerWeek": 3, "conversionRate": 0.082 }
],
"estimatedImpact": "Applying this cap would optimize contact frequency for better conversion."
},
"createdAt": "2026-03-18T10:00:00Z"
}
]
}
Roles
admin, editor, viewer
POST /api/v1/ai/recommendations
Create a new AI recommendation manually.
Request Body
| Field | Type | Required | Description |
|---|
type | string | Yes | policy, rule, segment, content |
title | string | Yes | Short summary |
description | string | Yes | Detailed explanation |
payload | object | No | Machine-readable entity definition |
confidence | string | No | high, medium, low. Default: medium |
source | string | No | llm, ml_worker. Default: llm |
metadata | object | No | Additional context data |
Response
201 Created with the created recommendation object.
Roles
admin, editor
GET /api/v1/ai/recommendations/
Get a single recommendation by ID.
Path Parameters
| Parameter | Type | Description |
|---|
id | string | Recommendation ID |
Response
{
"recommendation": { "id": "rec_001", "type": "policy", "title": "...", "status": "new", ... }
}
Roles
admin, editor, viewer
PATCH /api/v1/ai/recommendations/
Update the status of a recommendation.
Path Parameters
| Parameter | Type | Description |
|---|
id | string | Recommendation ID |
Request Body
| Field | Type | Required | Values |
|---|
status | string | Yes | reviewed, applied, dismissed |
Response
{
"recommendation": { "id": "rec_001", "status": "reviewed", ... }
}
Roles
admin, editor
POST /api/v1/ai/recommendations//apply
Apply an AI recommendation by creating the corresponding entity in draft status. The created entity type depends on the recommendation type.
Path Parameters
| Parameter | Type | Description |
|---|
id | string | Recommendation ID |
Entity Creation by Type
| Recommendation Type | Entity Created | Module |
|---|
policy | Contact Policy (draft) | Studio > Contact Policies |
rule | Qualification Rule (draft) | Studio > Qualification Rules |
segment | Customer Segment (draft) | AI > Segments |
content | Treatment / Creative (draft) | Studio > Treatments |
Response
{
"entityId": "cp_001",
"entityType": "policy"
}
The recommendation status is updated to applied with the appliedEntityId set to the created entity’s ID. Returns 400 if the recommendation has already been applied.
Roles
admin only
GET /api/v1/ai/ml-worker/status
Check ML Worker connectivity status. Checks the tenant’s ML Worker configuration from settings, with an environment variable fallback (ML_WORKER_URL). The health check endpoint (/health) uses a 30-second cache TTL and 3-second timeout.
Response — Connected
{
"connected": true,
"url": "https://ml-worker.internal:8080"
}
Response — Not Connected
Roles
admin, editor, viewer
GET /api/v1/ai/config-status
Returns whether the configured AI provider has the credentials it needs to run. The route at src/app/api/v1/ai/config-status/route.ts:9-21 reads the merged tenant + environment config via getAiConfigAsync(), then reports configured: true when the provider does not need an API key (ollama, lm_studio) or when an API key is present.
Response
{
"configured": true,
"provider": "openai"
}
true when the provider does not need a key (ollama or lm_studio) or a key is configured. false only when a key-required provider has no key set.
The active provider id from getAiConfigAsync(). Examples: "openai", "anthropic", "ollama", "lm_studio".
Roles
admin, editor, viewer
Example
curl https://playground.kaireonai.com/api/v1/ai/config-status \
-H "X-API-Key: krn_your_api_key" \
-H "X-Tenant-Id: 5a9904b9-..."
POST /api/v1/ai/imports//apply
Atomic apply of operator verdicts on AI-extracted import proposals. The route at src/app/api/v1/ai/imports/[id]/apply/route.ts wraps every verdict in a single prisma.$transaction (timeout 30s per lib/ai/import/apply.ts:55) — any per-verdict failure rolls back the entire batch and returns failedProposalId so the UI can highlight the offending row inline.
The path parameter id is the attachmentId (the chat attachment that produced the proposals), not the apply id.
Path Parameters
| Parameter | Type | Description |
|---|
id | string | AiAttachment.id — the document the proposals were extracted from. |
Request Body
Validated by Schema at apply/route.ts:20-30.
AI conversation id the proposals were generated in. Used for audit linkage.
Non-empty array of { proposalId, verdict } items. verdict is one of "create-new", "merge-into-existing", or "skip".
Verdict semantics
| Verdict | Behavior |
|---|
create-new | Insert a new entity row from the proposal payload. The new row id is recorded in AiImportApply.createdEntityIds so revert can delete it. |
merge-into-existing | Update an existing entity. The field-level diff is recorded in AuditLog; revert does NOT undo merges (apply.ts:636). |
skip | No entity write. A SkipDigest row is recorded so the same proposal does not reappear in future extracts. |
Response
Returns the ApplyOk shape declared at lib/ai/import/apply.ts:39-45 on success and ApplyFail at lib/ai/import/apply.ts:47-53 on rollback.
{
"ok": true,
"applyId": "apl_abc123",
"createdEntityIds": [
{ "entityType": "offer", "entityId": "off_xyz" }
],
"mergedEntityIds": [
{ "entityType": "creative", "entityId": "crv_def" }
],
"skippedProposalIds": ["prop_111", "prop_222"]
}
Rollback response:
{
"ok": false,
"status": "rolled_back",
"failedProposalId": "prop_888",
"errorMessage": "creative.templateType is required",
"errorField": "templateType"
}
Status codes
| Code | When |
|---|
| 201 | Apply succeeded |
| 400 | Invalid JSON body or schema validation failure |
| 401 | Caller is not authenticated |
| 403 | Caller is not admin or editor |
| 422 | Per-verdict failure rolled back the whole transaction |
| 500 | Unexpected error in runApply |
Honest limit
Apply itself does not call the LLM — the customer cost was already incurred at extract time. See AI Document Import.
Roles
admin, editor
POST /api/v1/ai/imports//revert
Soft-revert an apply: deletes the rows in AiImportApply.createdEntityIds, leaves merges as-is, and refuses to cascade-delete a created entity that now has child references. The route at src/app/api/v1/ai/imports/[id]/revert/route.ts calls runRevert from lib/ai/import/apply.ts:564 and surfaces orphaned refusals in the response so the operator can resolve them manually.
The path parameter id is the applyId returned from the apply call — NOT the attachment id.
Path Parameters
| Parameter | Type | Description |
|---|
id | string | AiImportApply.id from the prior apply response. |
Request Body
None — revert is a single transactional sweep keyed by applyId.
Response
{
"ok": true,
"applyId": "apl_abc123",
"deletedEntityIds": [
{ "entityType": "offer", "entityId": "off_xyz" }
],
"skippedMerges": [
{ "entityType": "creative", "entityId": "crv_def" }
],
"orphanedRefusals": [
{
"entityType": "offer",
"entityId": "off_old",
"reason": "Foreign key constraint: 14 child interactionHistory rows reference this offer"
}
]
}
Entities that were created by the apply and successfully deleted. Each entry is { entityType, entityId }.
Entities that were merge-updated by the apply. Revert does NOT undo these — operators do field-level undo manually against the AuditLog field-diff trail (apply.ts:636).
Created entities that now have child references and cannot be cascade-deleted. The reason field contains the truncated FK error message.
Status codes
| Code | When | Source |
|---|
| 200 | Revert succeeded | route.ts:34 |
| 401 / 403 | Caller is not admin or editor | requireAuth at lib/require-auth.ts |
| 404 | applyId does not match a row for the tenant | revert/route.ts:29-30 |
| 409 | Apply was already reverted | revert/route.ts:31 |
| 500 | Unexpected error in runRevert | revert/route.ts:32 |
Roles
admin, editor
Role Summary
| Endpoint | Method | Allowed Roles |
|---|
/api/v1/ai/chat | POST | admin, editor, viewer |
/api/v1/ai/conversations | GET | session auth |
/api/v1/ai/conversations | POST | session auth |
/api/v1/ai/conversations/{id} | GET | session auth |
/api/v1/ai/conversations/{id} | DELETE | admin, editor |
/api/v1/ai/analyze/{type} | POST | admin, editor |
/api/v1/ai/analyzer-settings | GET | admin, editor, viewer |
/api/v1/ai/analyzer-settings | PUT | admin |
/api/v1/ai/intelligence | POST | admin, editor, viewer |
/api/v1/ai/parse-rule | POST | admin, editor |
/api/v1/ai/recommendations | GET | admin, editor, viewer |
/api/v1/ai/recommendations | POST | admin, editor |
/api/v1/ai/recommendations/{id} | GET | admin, editor, viewer |
/api/v1/ai/recommendations/{id} | PATCH | admin, editor |
/api/v1/ai/recommendations/{id}/apply | POST | admin |
/api/v1/ai/ml-worker/status | GET | admin, editor, viewer |
/api/v1/ai/config-status | GET | admin, editor, viewer |
/api/v1/ai/imports/{id}/apply | POST | admin, editor |
/api/v1/ai/imports/{id}/revert | POST | admin, editor |
See also: AI Assistant | AI Insights | AI Configuration | AI Document Import | MCP Server