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:

  1. Install the SDK
    pip install iknowhy
  2. 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_....

  3. 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_here

Key types

TypeFormatUse
API Keycwy_...Authenticate SDK calls and direct API requests
Admin KeySet via ADMIN_KEY env varCreate 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

name string Required. Event type name. Max 128 chars. e.g. tool_call, decision.
payload object Optional. Arbitrary JSON data attached to the event. Max 50 keys.
session_id string Optional. Groups events into a session. Max 128 chars.
cause_ids list[string] Optional. IDs of events that caused this one. Max 20 items. This builds the causal graph.
tags object Optional. Key-value metadata for filtering and grouping.

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

ParamTypeDefaultDescription
session_idstringFilter by session
namestringFilter by event name
limitint100Max 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

ConstraintRule
tool_call_needs_resultEvery tool_call must have a corresponding tool_result that references it
error_needs_handlingEvery error event must be followed by an error_handling event
external_send_needs_decisionEvery 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 NameWhen to UseRequired Cause
user_requestUser initiates an action or taskNone (root event)
decisionAgent chooses an action to takeAny (typically user_request)
tool_callAgent invokes a toolTypically decision
tool_resultTool returns a responsetool_call (enforced by constraint)
errorSomething went wrongAny
error_handlingRecovery or mitigation of an errorerror (enforced by constraint)
external_sendAgent sends data externally (email, API, etc.)decision (enforced by constraint)
constraint_checkA policy or guardrail was evaluatedAny

Rate Limits

LimitFreePro
Events per month10,0001,000,000
API keys110
Data retention7 days90 days
Event writes1,000 / min1,000 / min
Event reads300 / min300 / min

When you exceed your monthly event quota, the API returns 402. Rate limit bursts return 429. No surprise charges — ever.

Error Codes

CodeCauseFix
401Missing or invalid API keyCheck your Authorization: Bearer cwy_... header
402Monthly event quota exceededUpgrade to Pro or wait for quota reset
422Invalid request body (bad field types, missing required fields)Check the field reference above — name is required, max lengths apply
429Rate limit exceeded (too many requests per minute)Back off and retry. Writes: 1,000/min, reads: 300/min