LLM Assistant
The Kanoniv LLM assistant provides a natural language interface to your identity graph. Ask questions about entities, plan agent workflows, get explanations for merge decisions, and generate identity specs from plain English.
Cloud-only
The LLM endpoints require Kanoniv Cloud. Plan and execute endpoints require admin role.
PII-safe
Entity data (names, emails, phones) is never sent to the LLM. The assistant only sees structural metadata: entity IDs, source names, match scores, event types, and aggregate counts.
Capabilities
| Capability | Endpoint | Description |
|---|---|---|
| Chat | POST /v1/llm/chat | Multi-turn conversational assistant with SSE streaming |
| Plan | POST /v1/llm/plan | Generate agent execution plans from intent |
| Explain | POST /v1/llm/explain | Evidence-based explanations for merge decisions |
| Generate Spec | POST /v1/llm/generate-spec | Create identity specs from natural language |
Chat
Multi-turn conversational chat with SSE (Server-Sent Events) streaming. The assistant has context about your tenant: sources, entity counts, agent configs, recent runs, and feedback labels.
Request
curl -N -X POST https://api.kanoniv.com/v1/llm/chat \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"message": "How many entities do I have and which sources contribute the most?",
"conversation_id": null
}'| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | Your question (1-10,000 chars) |
conversation_id | UUID | No | Continue an existing conversation |
SSE Response
The response streams as Server-Sent Events:
event: conversation_id
data: 550e8400-e29b-41d4-a716-446655440000
event: text
data: You currently have 4,200 canonical entities across 3 sources.
event: text
data: The CRM source contributes 2,100 records (50%), billing has 1,400 (33%),
event: text
data: and the support source has 700 (17%).
event: usage
data: {"input_tokens": 1250, "output_tokens": 89}
event: done
data:| Event | Data | Description |
|---|---|---|
conversation_id | UUID | Conversation ID (first event) |
text | string | Streaming text delta |
usage | JSON | Token counts: input_tokens, output_tokens |
done | empty | Stream complete |
error | string | Error message (replaces done) |
Example: Multi-turn conversation
# First message - creates a new conversation
curl -N -X POST https://api.kanoniv.com/v1/llm/chat \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"message": "What agents are currently enabled?"}'
# Response streams, includes conversation_id: 550e8400-...
# Follow-up - continues the conversation
curl -N -X POST https://api.kanoniv.com/v1/llm/chat \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"message": "Enable quality_monitor too. What settings do you recommend?",
"conversation_id": "550e8400-e29b-41d4-a716-446655440000"
}'CLI usage
The CLI provides a streaming terminal experience:
# Interactive chat
kanoniv ask "What agents are running?"
# Continue a conversation
kanoniv ask "What about auto_split?" --conversation 550e8400-...Plan
Generate structured agent execution plans from a natural language intent. The LLM produces a DAG (directed acyclic graph) of agent steps with dependencies, then you can execute the approved plan.
Admin only
Plan generation and execution require admin role.
Generate a plan
curl -X POST https://api.kanoniv.com/v1/llm/plan \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"intent": "Onboard my new Snowflake data and set up automated deduplication",
"scope": "snowflake source only"
}'| Field | Type | Required | Description |
|---|---|---|---|
intent | string | Yes | What you want to accomplish (1-5,000 chars) |
scope | string | No | Optional filter or context (0-1,000 chars) |
Plan response
{
"plan": {
"goal": "Onboard Snowflake data and set up automated deduplication",
"scope": "snowflake source only",
"steps": [
{
"agent": "auto_discover",
"params": { "sample_size": 1000 },
"depends_on": [],
"require_approval": false,
"reason": "Profile the Snowflake data to detect identity signals"
},
{
"agent": "auto_bootstrap",
"params": { "sensitivity": 0.5, "auto_apply": false },
"depends_on": ["auto_discover"],
"require_approval": true,
"reason": "Generate identity plan from discovered signals"
},
{
"agent": "scheduled_reconcile",
"params": {},
"depends_on": ["auto_bootstrap"],
"require_approval": false,
"reason": "Run initial batch reconciliation"
},
{
"agent": "quality_monitor",
"params": { "merge_rate_drop_threshold": 0.05 },
"depends_on": ["scheduled_reconcile"],
"require_approval": false,
"reason": "Monitor reconciliation quality going forward"
}
],
"estimated_duration_minutes": 15,
"risks": [
"Bootstrap may produce conservative thresholds if sample data is sparse"
],
"prerequisites": [
"Snowflake data must be ingested before running discovery"
]
},
"conversation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}Execute the plan
After reviewing the plan, execute it:
curl -X POST https://api.kanoniv.com/v1/llm/plan/execute \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"plan": { ... }
}'The planner executes steps in topological order, respecting depends_on dependencies. Steps with require_approval: true are queued for human review. Downstream steps wait until approved steps complete.
Execution response
{
"execution_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "awaiting_approval",
"results": [
{ "agent": "auto_discover", "status": "queued", "run_id": "..." },
{ "agent": "auto_bootstrap", "status": "requires_approval", "run_id": null },
{ "agent": "scheduled_reconcile", "status": "skipped", "error": "dependency not met" },
{ "agent": "quality_monitor", "status": "skipped", "error": "dependency not met" }
]
}| Execution status | Meaning |
|---|---|
executing | All steps queued or running |
awaiting_approval | Some steps need human approval before proceeding |
partial | Some steps failed |
CLI usage
# Generate a plan
kanoniv ask "Set up dedup for my CRM data" --plan
# The CLI prints the plan and conversation ID
# Execute by approving agent actions:
kanoniv agent actions --status pending
kanoniv agent approve ACTION_IDExplain
Get evidence-based explanations for merge decisions, entity history, cluster composition, or quality metrics. The assistant detects what kind of question you are asking and assembles the relevant context.
Request
curl -X POST https://api.kanoniv.com/v1/llm/explain \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"question": "Why were these two entities merged?",
"entity_ids": [
"550e8400-e29b-41d4-a716-446655440000",
"6ba7b810-9dad-11d1-80b4-00c04fd430c8"
]
}'| Field | Type | Required | Description |
|---|---|---|---|
question | string | Yes | Your question (1-5,000 chars) |
entity_ids | UUID[] | No | Entity IDs relevant to the question |
cluster_id | UUID | No | Cluster to analyze |
Question type detection
The assistant automatically detects the question type and assembles appropriate evidence:
| Question Type | Detected When | Evidence Used |
|---|---|---|
merge_explanation | Question mentions "merge", "combined", "same person", "duplicate", "why were" + entity_ids provided | Match audit scores, rule traces, merge history |
entity_history | Question mentions "history", "timeline", "happened to", "lifecycle" + entity_ids provided | Entity events, change audit trail |
cluster_analysis | cluster_id provided | Cluster members, relationships, source distribution |
quality_report | Question mentions "quality", "metric", "regression", "merge rate", "conflict" | Quality monitor runs, pattern memory entries |
general | None of the above | Tenant-level statistics |
Response
{
"explanation": "These two entities were merged because they share an exact email match ([email protected], score 1.0) and a high Jaro-Winkler name similarity (0.92 between 'Alice Smith' and 'A. Smith'). The composite Fellegi-Sunter score of 0.97 exceeded the merge threshold of 0.9. The merge was executed by the auto_merge agent on 2026-02-20.",
"evidence": [
{
"type": "match_score",
"rule": "email_exact",
"score": 1.0,
"weight": 1.0
},
{
"type": "match_score",
"rule": "name_fuzzy",
"score": 0.92,
"weight": 0.8
},
{
"type": "merge_event",
"actor": "auto_merge",
"timestamp": "2026-02-20T14:30:00Z"
}
],
"question_type": "merge_explanation"
}Example: Quality investigation
curl -X POST https://api.kanoniv.com/v1/llm/explain \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{"question": "Why did the merge rate drop 8% in the last run?"}'Response:
{
"explanation": "The merge rate dropped from 56.2% to 48.1% in the latest run. This correlates with the ingestion of 1,200 new support ticket records that have sparse identity fields (only 34% have email populated vs 95% in your CRM source). The quality_monitor agent flagged this as a degradation event. Consider running auto_tune to recalibrate thresholds with the new data distribution.",
"evidence": [...],
"question_type": "quality_report"
}CLI usage
# Explain a merge
kanoniv ask "Why were these merged?" \
--entity-ids 550e8400-...,6ba7b810-...
# Quality question
kanoniv ask "Why did merge rate drop?"Generate Spec
Generate a valid Kanoniv identity spec YAML from a natural language description. The assistant creates the spec, validates it, and retries up to 2 times if validation fails.
Request
curl -X POST https://api.kanoniv.com/v1/llm/generate-spec \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"description": "Match customers across CRM and billing by email (exact), phone (exact), and name (fuzzy). Use email for blocking. Merge threshold 0.9.",
"source_names": ["crm", "billing"]
}'| Field | Type | Required | Description |
|---|---|---|---|
description | string | Yes | What the spec should do (1-5,000 chars) |
source_names | string[] | No | Existing sources to include |
Response
{
"spec_yaml": "api_version: kanoniv/v1\nentity:\n name: customer\nsources:\n - name: crm\n id_field: id\n - name: billing\n id_field: id\nblocking:\n - email\nmatching:\n - fields: [email]\n comparator: exact\n weight: 1.0\n - fields: [phone]\n comparator: exact\n weight: 0.9\n - fields: [first_name, last_name]\n comparator: jaro_winkler\n weight: 0.85\ndecision:\n merge_threshold: 0.9\n review_threshold: 0.7\n",
"validation_errors": [],
"retries": 0
}If validation fails, the response includes errors and a retry count:
{
"spec_yaml": "...",
"validation_errors": ["blocking: key 'ssn' not found in any source field"],
"retries": 1
}Example: Generate from a detailed description
kanoniv ask "Create a spec for matching patient records across hospital, lab, and pharmacy systems. Match on MRN (exact), SSN last 4 + DOB (composite), and name + DOB (fuzzy). Block on DOB and SSN last 4. Merge at 0.92, review at 0.75." --generate-spec --sources hospital,lab,pharmacyCLI usage
# Simple spec
kanoniv ask "Match leads by email and company name" --generate-spec
# With specific sources
kanoniv ask "Deduplicate across CRM and support" --generate-spec --sources crm,supportConversations API
Chat conversations are persisted server-side. Use the conversations API to list, retrieve, or delete them.
List conversations
curl https://api.kanoniv.com/v1/llm/conversations?limit=10 \
-H "X-API-Key: your-key"Returns metadata only (no messages) for fast loading:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "How many entities do I have",
"messages": [],
"total_tokens_used": 1339,
"created_at": "2026-02-22T10:00:00Z",
"updated_at": "2026-02-22T10:05:00Z"
}
]| Query Param | Type | Default | Max | Description |
|---|---|---|---|---|
limit | int | 20 | 100 | Number of conversations |
offset | int | 0 | - | Pagination offset |
Get conversation
curl https://api.kanoniv.com/v1/llm/conversations/550e8400-... \
-H "X-API-Key: your-key"Returns full conversation including all messages:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "How many entities do I have",
"messages": [
{ "role": "user", "content": "How many entities do I have?" },
{ "role": "assistant", "content": "You currently have 4,200 canonical entities..." },
{ "role": "user", "content": "Which source contributes the most?" },
{ "role": "assistant", "content": "The CRM source contributes 2,100 records (50%)..." }
],
"total_tokens_used": 1339,
"created_at": "2026-02-22T10:00:00Z",
"updated_at": "2026-02-22T10:05:00Z"
}Delete conversation
curl -X DELETE https://api.kanoniv.com/v1/llm/conversations/550e8400-... \
-H "X-API-Key: your-key"Memory
Every LLM action writes an audit entry to the memory system for traceability:
| Action | Entry Type | What's Recorded |
|---|---|---|
| Plan | decision | Goal, step count, agent types |
| Execute Plan | decision | Execution ID, status, step results |
| Explain | investigation | Question type, entity IDs, token usage |
| Generate Spec | decision | Description, spec size, retry count |
Memory entries can be queried via the API or MCP resources:
# Via API
curl https://api.kanoniv.com/v1/memory?entry_type=decision&limit=10 \
-H "X-API-Key: your-key"
# Via MCP resource
# kanoniv://memory/decisionsEndpoint Summary
| Method | Path | Auth | Role | Description |
|---|---|---|---|---|
POST | /v1/llm/chat | JWT | Any | SSE streaming chat |
POST | /v1/llm/plan | JWT | Admin | Generate agent plan |
POST | /v1/llm/plan/execute | JWT | Admin | Execute approved plan |
POST | /v1/llm/explain | JWT | Any | Evidence-based explanation |
POST | /v1/llm/generate-spec | JWT | Any | Generate spec from description |
GET | /v1/llm/conversations | JWT | Any | List conversations |
GET | /v1/llm/conversations/:id | JWT | Any | Get conversation with messages |
DELETE | /v1/llm/conversations/:id | JWT | Any | Delete conversation |
