Documentation
Everything you need to instrument your AI agent with causal tracing. Get a key, log events, and trace any outcome back to its root cause.
Getting Started
You can be up and running in under a minute. Three steps:
-
Install the SDK
pip install iknowhy -
Get an API key
Sign up at iknowhy.ai/signup to get a free key, or generate one from your account page. Keys look like
cwy_.... -
Log your first event
# Connect and log one event from iknowhy.sdk import CausewayClient c = CausewayClient(api_key="cwy_your_key_here") event_id = c.log("user_request", payload={"task": "search docs"})
Authentication
All API requests require a Bearer token in the Authorization header. Pass your API key like this:
Authorization: Bearer cwy_your_key_hereKey types
| Type | Format | Use |
|---|---|---|
| API Key | cwy_... | Authenticate SDK calls and direct API requests |
| Admin Key | Set via ADMIN_KEY env var | Create new API keys via POST /api/keys |
Log an Event
POST /api/events — Create a causal event node. Every event can optionally reference one or more cause events to build the causal DAG.
Request fields
tool_call, decision.
Response
{
"event_id": "evt_abc123...",
"name": "tool_call",
"ts": 1718300400.123
}curl example
curl -X POST https://iknowhy.ai/api/events \
-H "Authorization: Bearer cwy_your_key" \
-H "Content-Type: application/json" \
-d '{
"name": "tool_call",
"payload": {"tool": "web_search", "query": "revenue Q4"},
"session_id": "sess_001",
"cause_ids": ["evt_parent_id"]
}'Python example
from iknowhy.sdk import CausewayClient
c = CausewayClient(api_key="cwy_your_key")
# Log a chain: request → decision → tool call → result
req = c.log("user_request", payload={"task": "search"}, session_id="s1")
dec = c.log("decision", payload={"action": "use web_search"}, cause_ids=[req], session_id="s1")
call = c.log("tool_call", payload={"tool": "web_search"}, cause_ids=[dec], session_id="s1")
res = c.log("tool_result", payload={"status": "ok", "n": 3}, cause_ids=[call], session_id="s1")Query Events
GET /api/events — Retrieve events with optional filters.
Query parameters
| Param | Type | Default | Description |
|---|---|---|---|
session_id | string | — | Filter by session |
name | string | — | Filter by event name |
limit | int | 100 | Max events to return (1–1000) |
Response
{
"events": [
{
"id": "evt_abc123",
"name": "tool_call",
"session_id": "sess_001",
"payload": {"tool": "web_search"},
"cause_ids": ["evt_parent"],
"ts": 1718300400.123,
"tags": {}
}
]
}Causal Chains
GET /api/chain/{event_id} — Walk backwards through the causal DAG from any event to its root cause. This is the core feature of iKnowhy: given any outcome, you can trace exactly what caused it.
The chain follows cause_ids links recursively. If event C was caused by B, and B was caused by A, querying the chain for C returns [A, B, C] — the full causal ancestry in order.
Response
{
"chain": [
{"id": "evt_001", "name": "user_request", "ts": 1718300400.0, ...},
{"id": "evt_002", "name": "decision", "ts": 1718300400.5, ...},
{"id": "evt_003", "name": "tool_call", "ts": 1718300401.0, ...}
],
"length": 3
}Example
# Trace why a tool was called
chain = c.chain("evt_003")
for event in chain:
print(f"{event['name']} → {event.get('payload', {})}")Constraint Violations
GET /api/violations — Check the last 500 events for built-in causal constraint violations. Constraints enforce structural rules that healthy agent behaviour should follow.
Built-in constraints
| Constraint | Rule |
|---|---|
tool_call_needs_result | Every tool_call must have a corresponding tool_result that references it |
error_needs_handling | Every error event must be followed by an error_handling event |
external_send_needs_decision | Every external_send must be caused by a decision event |
Response
{
"violations": [
{
"constraint": "tool_call_needs_result",
"event_id": "evt_abc123",
"description": "tool_call has no matching tool_result"
}
],
"count": 1
}Sessions
GET /api/sessions — List all sessions for your tenant. Returns the 100 most recent sessions with event counts and timestamps.
Response
{
"sessions": [
{
"session_id": "sess_001",
"started_at": 1718300400.0,
"ended_at": 1718301000.0,
"event_count": 42
}
]
}Python SDK
The CausewayClient class wraps the REST API for convenient Python usage. Install with pip install iknowhy.
Constructor
from iknowhy.sdk import CausewayClient
c = CausewayClient(
api_key="cwy_your_key", # required
base_url="https://iknowhy.ai" # default: http://localhost:8100
)Methods
log(name, payload=None, session_id=None, cause_ids=None, tags=None) → str
Log a causal event. Returns the event_id.
eid = c.log("decision", payload={"action": "approve"}, session_id="s1")
# Returns: "evt_abc123..."events(session_id=None, name=None, limit=100) → list
Fetch events with optional filters.
all_calls = c.events(name="tool_call", limit=50)chain(event_id) → list
Get the full causal ancestry for an event.
ancestry = c.chain("evt_abc123")
for e in ancestry:
print(e["name"], e["payload"])violations() → list
Check for constraint violations across recent events.
issues = c.violations()
if issues:
print(f"Found {len(issues)} violations")sessions() → list
List all sessions.
for s in c.sessions():
print(s["session_id"], s["event_count"])MCP Integration
iKnowhy provides an MCPEventAdapter that wraps any MCP (Model Context Protocol) server to automatically capture tool calls, results, and context changes as causal events.
Usage
from iknowhy.sdk import CausewayClient
from iknowhy.mcp import MCPEventAdapter
# Wrap your MCP server
client = CausewayClient(api_key="cwy_your_key")
adapter = MCPEventAdapter(client, session_id="mcp-session-1")
# Every tool call through the adapter is automatically
# logged as a causal event with proper cause_ids linking
result = adapter.call_tool("web_search", {"query": "revenue"})
# The adapter logs:
# 1. tool_call → payload: {tool: "web_search", ...}
# 2. tool_result → payload: {status: 200, ...}, cause_ids: [tool_call_id]The adapter handles causal linking automatically — every tool_result references its tool_call, and context window changes are captured as discrete events in the causal graph.
Event Type Reference
You can use any event name, but these built-in types have special meaning for constraints and chain analysis:
| Event Name | When to Use | Required Cause |
|---|---|---|
user_request | User initiates an action or task | None (root event) |
decision | Agent chooses an action to take | Any (typically user_request) |
tool_call | Agent invokes a tool | Typically decision |
tool_result | Tool returns a response | tool_call (enforced by constraint) |
error | Something went wrong | Any |
error_handling | Recovery or mitigation of an error | error (enforced by constraint) |
external_send | Agent sends data externally (email, API, etc.) | decision (enforced by constraint) |
constraint_check | A policy or guardrail was evaluated | Any |
Rate Limits
| Limit | Free | Pro |
|---|---|---|
| Events per month | 10,000 | 1,000,000 |
| API keys | 1 | 10 |
| Data retention | 7 days | 90 days |
| Event writes | 1,000 / min | 1,000 / min |
| Event reads | 300 / min | 300 / min |
When you exceed your monthly event quota, the API returns 402. Rate limit bursts return 429. No surprise charges — ever.
Error Codes
| Code | Cause | Fix |
|---|---|---|
401 | Missing or invalid API key | Check your Authorization: Bearer cwy_... header |
402 | Monthly event quota exceeded | Upgrade to Pro or wait for quota reset |
422 | Invalid request body (bad field types, missing required fields) | Check the field reference above — name is required, max lengths apply |
429 | Rate limit exceeded (too many requests per minute) | Back off and retry. Writes: 1,000/min, reads: 300/min |