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.evals—Rubric.load(path),judge_from_spec("anthropic:claude-opus-4-8"),run(rubric, client, judge); CLIlooptail-evals run. See evaluators.looptail.issues—cluster(client)groups failing verdicts by (rubric, weakest criterion) into trackedissueevents;latest_issues(client). CLIlooptail-issues cluster --app <name>. See the Improve loop.looptail.improve—propose · replay · canary · approve, each recorded as a signedimprove/approveevent; canary and approve are gated on a passing replay. CLIlooptail-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) → stringlt.lastEventId— id of the most recent event, ornull.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 signedevaluateevents. See evaluators.cluster(lt)/latestIssues(lt)— group failing verdicts into trackedissueevents. See the Improve loop.propose/replay/canary/approve— the Improve loop, each step a signedimprove/approveevent; 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. NeedsLOOPTAIL_API_KEY; endpoint via--endpointorLOOPTAIL_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:
0ok ·1verification failed ·2usage 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_KEY | enables hosted sync + hosted evals (private beta) |
|---|---|
LOOPTAIL_TRAIL_DIR | trail directory; default ./.looptail |
LOOPTAIL_SIGNING_KEY | hex Ed25519 seed; overrides the key file |
~/.looptail/signing-key | created on first use, mode 0600 |
.looptail/<app>.jsonl | the 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.