Skip to content

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

CapabilityEndpointDescription
ChatPOST /v1/llm/chatMulti-turn conversational assistant with SSE streaming
PlanPOST /v1/llm/planGenerate agent execution plans from intent
ExplainPOST /v1/llm/explainEvidence-based explanations for merge decisions
Generate SpecPOST /v1/llm/generate-specCreate 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

bash
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
  }'
FieldTypeRequiredDescription
messagestringYesYour question (1-10,000 chars)
conversation_idUUIDNoContinue 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:
EventDataDescription
conversation_idUUIDConversation ID (first event)
textstringStreaming text delta
usageJSONToken counts: input_tokens, output_tokens
doneemptyStream complete
errorstringError message (replaces done)

Example: Multi-turn conversation

bash
# 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:

bash
# 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

bash
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"
  }'
FieldTypeRequiredDescription
intentstringYesWhat you want to accomplish (1-5,000 chars)
scopestringNoOptional filter or context (0-1,000 chars)

Plan response

json
{
  "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:

bash
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

json
{
  "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 statusMeaning
executingAll steps queued or running
awaiting_approvalSome steps need human approval before proceeding
partialSome steps failed

CLI usage

bash
# 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_ID

Explain

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

bash
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"
    ]
  }'
FieldTypeRequiredDescription
questionstringYesYour question (1-5,000 chars)
entity_idsUUID[]NoEntity IDs relevant to the question
cluster_idUUIDNoCluster to analyze

Question type detection

The assistant automatically detects the question type and assembles appropriate evidence:

Question TypeDetected WhenEvidence Used
merge_explanationQuestion mentions "merge", "combined", "same person", "duplicate", "why were" + entity_ids providedMatch audit scores, rule traces, merge history
entity_historyQuestion mentions "history", "timeline", "happened to", "lifecycle" + entity_ids providedEntity events, change audit trail
cluster_analysiscluster_id providedCluster members, relationships, source distribution
quality_reportQuestion mentions "quality", "metric", "regression", "merge rate", "conflict"Quality monitor runs, pattern memory entries
generalNone of the aboveTenant-level statistics

Response

json
{
  "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

bash
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:

json
{
  "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

bash
# 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

bash
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"]
  }'
FieldTypeRequiredDescription
descriptionstringYesWhat the spec should do (1-5,000 chars)
source_namesstring[]NoExisting sources to include

Response

json
{
  "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:

json
{
  "spec_yaml": "...",
  "validation_errors": ["blocking: key 'ssn' not found in any source field"],
  "retries": 1
}

Example: Generate from a detailed description

bash
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,pharmacy

CLI usage

bash
# 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,support

Conversations API

Chat conversations are persisted server-side. Use the conversations API to list, retrieve, or delete them.

List conversations

bash
curl https://api.kanoniv.com/v1/llm/conversations?limit=10 \
  -H "X-API-Key: your-key"

Returns metadata only (no messages) for fast loading:

json
[
  {
    "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 ParamTypeDefaultMaxDescription
limitint20100Number of conversations
offsetint0-Pagination offset

Get conversation

bash
curl https://api.kanoniv.com/v1/llm/conversations/550e8400-... \
  -H "X-API-Key: your-key"

Returns full conversation including all messages:

json
{
  "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

bash
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:

ActionEntry TypeWhat's Recorded
PlandecisionGoal, step count, agent types
Execute PlandecisionExecution ID, status, step results
ExplaininvestigationQuestion type, entity IDs, token usage
Generate SpecdecisionDescription, spec size, retry count

Memory entries can be queried via the API or MCP resources:

bash
# 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/decisions

Endpoint Summary

MethodPathAuthRoleDescription
POST/v1/llm/chatJWTAnySSE streaming chat
POST/v1/llm/planJWTAdminGenerate agent plan
POST/v1/llm/plan/executeJWTAdminExecute approved plan
POST/v1/llm/explainJWTAnyEvidence-based explanation
POST/v1/llm/generate-specJWTAnyGenerate spec from description
GET/v1/llm/conversationsJWTAnyList conversations
GET/v1/llm/conversations/:idJWTAnyGet conversation with messages
DELETE/v1/llm/conversations/:idJWTAnyDelete conversation

The identity and delegation layer for AI agents.