get started

SDK & CLI reference

The whole surface, on one page. Both SDKs write the sameopen trail format, so trails are portable and cross-verifiable.

Python — looptail

import looptail

client = looptail.init(
    app="support-agent",       # required; trail file is .looptail/<app>.jsonl
    api_key=None,              # optional; enables hosted sync (private beta)
    trail_dir=None,            # default: LOOPTAIL_TRAIL_DIR or ./.looptail
    endpoint=None,             # hosted ingest override
    key_path=None,             # default: ~/.looptail/signing-key
)
  • @looptail.trail — decorate a sync or async function; every call is recorded (name, args, result or error, duration). Exceptions re-raise.
  • looptail.event(kind, body, ref=None) → str — record any loop event; kinds: observe · evaluate · issue · improve · approve · outcome.
  • looptail.outcome(event_id, **signals) → str — link a signal (csat, resolved, …) back to an earlier event.
  • looptail.last_event_id() → str | None — id of the most recent event.
  • looptail.verify_trail(app=None, trail_dir=None) → dict{ok, count, errors, key_changes}.
  • looptail.instrument.anthropic(client) / .openai(client) — wrap a provider client in place; every call becomes an observe event. See integrations.
  • looptail.evalsRubric.load(path), judge_from_spec("anthropic:claude-opus-4-8"), run(rubric, client, judge); CLI looptail-evals run. See evaluators.
  • looptail.issuescluster(client) groups failing verdicts by (rubric, weakest criterion) into tracked issue events; latest_issues(client). CLI looptail-issues cluster --app <name>. See the Improve loop.
  • looptail.improvepropose · replay · canary · approve, each recorded as a signed improve/approve event; canary and approve are gated on a passing replay. CLI looptail-improve. See the Improve loop.

TypeScript — @looptail/sdk

import { Looptail } from '@looptail/sdk';

const lt = new Looptail({
  app: 'support-agent',        // required
  apiKey: undefined,           // optional; or LOOPTAIL_API_KEY
  trailDir: undefined,         // default: LOOPTAIL_TRAIL_DIR or ./.looptail
  endpoint: undefined,         // hosted ingest override
  keyPath: undefined,          // default: ~/.looptail/signing-key
});
  • lt.trail(fn) — wrap a sync or async function; returns the same signature.
  • lt.event(kind, body, ref?) → string · lt.outcome(eventId, signals) → string
  • lt.lastEventId — id of the most recent event, or null.
  • lt.verify() → { ok, count, errors, keyChanges }
  • lt.flush() → Promise<void> — drain the hosted-sync queue before exit.
  • instrumentAnthropic(client, lt) / instrumentOpenAI(client, lt) — wrap a provider client in place; every call becomes an observe event.
  • loadRubric(path), judgeFromSpec("anthropic:claude-opus-4-8"), run(rubric, lt, judge) — score events into signed evaluate events. See evaluators.
  • cluster(lt) / latestIssues(lt) — group failing verdicts into tracked issue events. See the Improve loop.
  • propose / replay / canary / approve — the Improve loop, each step a signed improve/approve event; canary and approve gated on a passing replay. See the Improve loop.
  • Chain primitives are exported too: verifyChain, eventHash, canonical, SigningKey, TrailStore.

The Python and TypeScript SDKs are at full parity — Observe, Understand, and Improve — with the same rubric prompt, judge schema, and event shapes, so a JS verdict, a Python verdict, and a hosted verdict all agree.

CLI — @looptail/cli

npx @looptail/cli verify [--app <name>] [--trail-dir <dir>] [--since 24h] [--anchors] [--json]
npx @looptail/cli export [--app <name>] [--trail-dir <dir>] [--out <file.zip>]
npx @looptail/cli evals run --rubric <file.json> --app <name> [--judge provider:model] [--all]
npx @looptail/cli issues cluster --app <name> [--trail-dir <dir>]
npx @looptail/cli improve <propose|replay|canary|approve> --app <name> …
  • --app — defaults to the only trail in the directory.
  • --since 30m | 24h | 30d — count events in the window. The full chain is always verified; integrity is global.
  • --anchors — also fetch the server's signed chain-head receipts and check them against the local chain: receipt signatures must verify, and every anchored head must exist locally. Needs LOOPTAIL_API_KEY; endpoint via --endpoint or LOOPTAIL_ENDPOINT.
  • --json — machine-readable result.
  • export — writes a dated, self-verifying evidence pack: the full chain, a manifest (head hash, writer keys, SHA-256), and recipient-runnable verification instructions. Refuses to export a broken chain.
  • Exit codes: 0 ok · 1 verification failed · 2 usage error.

Hosted API — ingest.looptail.ai (private beta)

With an API key, events sync to the hosted Tail, which re-verifies and anchors them. It also runs evaluators for you: upload a rubric and the platform judges new decisions on a schedule, recording server-signed verdicts you can read back.

# store a rubric for an app
curl -X POST https://ingest.looptail.ai/v1/rubrics \
  -H "Authorization: Bearer $LOOPTAIL_API_KEY" -H 'content-type: application/json' \
  -d '{"app":"support-agent","name":"refund-policy","version":1,
       "criteria":["Quotes the 30-day window","Never over-promises a refund"],
       "pass_threshold":0.85}'

# read verdicts + pass-rate (judged continuously, no code to run)
curl https://ingest.looptail.ai/v1/evaluations/support-agent \
  -H "Authorization: Bearer $LOOPTAIL_API_KEY"
  • POST /v1/rubrics · GET /v1/rubrics/{app} — manage rubrics (idempotent per name+version).
  • GET /v1/evaluations/{app} — verdicts + a pass-rate summary; each verdict is Ed25519-signed by the server key (verifiable like a receipt, independent of your own chain).
  • POST /v1/events · GET /v1/trail/{app} · GET /v1/anchors/{app} · GET /v1/verify/{app} — sync, page, receipts, server-side verification.

Environment & files

LOOPTAIL_API_KEYenables hosted sync + hosted evals (private beta)
LOOPTAIL_TRAIL_DIRtrail directory; default ./.looptail
LOOPTAIL_SIGNING_KEYhex Ed25519 seed; overrides the key file
~/.looptail/signing-keycreated on first use, mode 0600
.looptail/<app>.jsonlthe trail: append-only, one signed event per line

Semantics that both SDKs guarantee: the local trail is written before any network call; sync is best-effort and never raises into your app; records are never edited in place.