SVSANNVIT
SDKs

SDKs

Client libraries agents call into to seal their activity into the ledger — Python, TypeScript, Go, Java, and C#.

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:

  • Observe, never intermediate — nothing here sits in a request path.
  • Never block — capturing an event is a synchronous buffer push; delivery happens async on a timer. (The one deliberate exception is uploading binary content — see Blobs below.)
  • Never crash the host — every public entry point is exception-safe; failures go to an on_error callback instead of propagating.
  • Never lose silently — if the buffer overflows, the drop is counted and reported as a lifecycle event once delivery recovers.
  • Capture verbatim — no normalization, no redaction. The server computes every hash; the SDK only reports what happened.

Language status

LanguagePackageCoverage
TypeScriptsdk/typescriptFull parity — manual API + Anthropic/OpenAI auto-instrumentation (the reference implementation)
Pythonsdk/pythonFull parity — manual API + Anthropic/OpenAI auto-instrumentation
Gosdk/goManual API
Javasdk/javaManual API
C#sdk/csharpManual 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.

Core concepts

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.
  • Idempotency — every event carries an idempotency key (its span id); the server dedups on (org, span_id), so retries after a failed delivery are always safe.

record() fields

FieldValues
action_typellm_call, tool_call, search, data_read, data_write, model_inference, lifecycle, error
statussuccess, failure, timeout, denied, escalated
trigger.typehuman, system, schedule — who/what initiated this
trigger_categoryStandards trigger vocabulary (ISO 24970 / prEN 18229-1) — set on monitoring-detected events (drift, bias, adversarial input) and oversight/config-change events
payloads[].kindprompt, response, artifact
retrievals[].source_typedatabase, document, web, api

Decision lineage

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.

Blobs

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 guarantees

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.