Every SDK captures AI agent activity in-process and delivers it to the ledger API, so your agent code stays otherwise unmodified. They all follow the same contract, regardless of language:
on_error callback instead of propagating.| Language | Package | Coverage |
|---|---|---|
| TypeScript | sdk/typescript | Full parity — manual API + Anthropic/OpenAI auto-instrumentation (the reference implementation) |
| Python | sdk/python | Full parity — manual API + Anthropic/OpenAI auto-instrumentation |
| Go | sdk/go | Manual API |
| Java | sdk/java | Manual API |
| C# | sdk/csharp | Manual API |
!NOTE None of these are published to a package registry (npm, PyPI, Maven, NuGet) yet — that's a launch-gate decision still pending. Each language page below covers how to pull the SDK in as it stands today.
For OTel-instrumented apps in Go, Java, or C#, point your existing exporter at the ledger's OTLP receiver (/v1/otlp/v1/traces) instead of reaching for a per-language span processor — every gen_ai.* span becomes a ledger event automatically, server-side.
These apply across every language:
endpoint — the ledger API's base URL (http://127.0.0.1:4010 for a local dev stack — see Hosting with Docker).ingest_key — a per-team key; tenancy resolves server-side from it. Get one from npm run seed:demo (dev, prints a demo key) or from the dashboard once you have a real org set up.agent_id — a URI naming the acting AI system, e.g. urn:agent:research-assistant.record(...) — capture one AI action: an LLM call, tool call, search, or data read/write. Synchronous and non-blocking; returns a span id.flush() — deliver buffered events now. Runs automatically on a timer (default every 2s) as well.(org, span_id), so retries after a failed delivery are always safe.record() fields| Field | Values |
|---|---|
action_type | llm_call, tool_call, search, data_read, data_write, model_inference, lifecycle, error |
status | success, failure, timeout, denied, escalated |
trigger.type | human, system, schedule — who/what initiated this |
trigger_category | Standards trigger vocabulary (ISO 24970 / prEN 18229-1) — set on monitoring-detected events (drift, bias, adversarial input) and oversight/config-change events |
payloads[].kind | prompt, response, artifact |
retrievals[].source_type | database, document, web, api |
Every event can reference the event that caused it via parent_ref (a span id), so a chain like search → LLM call that used it → tool call that acted on the result is reconstructable. Each SDK also exposes an ambient "current span" helper (within() / with()) so nested calls inherit the right parent automatically without threading an id through every function signature by hand.
Binary content (images, PDFs, audio) can't ride inline in record() as text. Upload it first — this is the one call in every SDK that blocks, since the blob must exist server-side before an event can reference it — then pass the returned blob id as a payload or retrieval's blob_ref. Uploads are content-addressed: re-uploading identical bytes returns the same blob id.
Delivery is at-least-once. A failed flush (network error, 5xx, 429) leaves the batch buffered for the next timer tick — nothing is dropped on a transient failure. A 401 means the ingest key is wrong; that's reported via on_error and the batch is left buffered rather than discarded. If the buffer itself fills up (default cap: 10,000 events), the oldest events are dropped to make room, but that drop is never silent — a counted-gap lifecycle event is sealed in ahead of the next batch so the gap itself is part of the audit trail.
Hosting on Kubernetes
Deploy the Sannvit Ledger to a client's Kubernetes cluster with the Helm chart, for horizontal scaling beyond a single host.
TypeScript SDK
Install and use the ledger's TypeScript SDK — the reference implementation, with manual capture and one-line Anthropic/OpenAI auto-instrumentation.