Integration guide
Fifteen minutes to a sealed ledger.
Five integration paths, all exercised against a live frontier model before this page was written. Pick the one that matches your stack; they all land in the same tamper-evident chain.
Sign up, create a project
One project per AI system. You get a project id and mint an API key with ingest scope from the dashboard - the key is shown once and stored only as a hash.
Point your traces at us
Pick a path below. The OTLP route is usually two environment variables; the SDK adds the richer compliance events when you want them.
Verify and attest
Watch sessions appear as sealed traces, re-verify the chain any time, and generate evidence packs mapped to EU AI Act articles when a buyer or auditor asks.
OpenTelemetry (any language)
You already emit OTel traces - most agent frameworks do.
# Any OpenTelemetry exporter, any language - two env vars: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://YOUR_HOST/v1/otlp/<projectId>/v1/traces OTEL_EXPORTER_OTLP_TRACES_HEADERS=authorization=Bearer atst_YOUR_KEY # Spans using the GenAI semantic conventions (gen_ai.*) map automatically: # gen_ai.operation.name = chat -> model.call # gen_ai.operation.name = execute_tool -> tool.call # gen_ai.operation.name = invoke_agent -> decision # Token usage (gen_ai.usage.*) is carried into the sealed payload.
Zero code change: set two environment variables and your existing GenAI spans become sealed ledger events.
Vercel AI SDK
TypeScript agents on the ai package.
import { generateText } from "ai";
import { createAzure } from "@ai-sdk/azure";
// Register any OTel NodeTracerProvider with an OTLP exporter
// pointed at your AttestAI project endpoint (see the env vars tab),
// then switch telemetry on. That is the whole integration:
const result = await generateText({
model: azure("gpt-5.5"),
tools: { policyLookup },
prompt: "Review claim CLM-1003 for approval.",
experimental_telemetry: { isEnabled: true },
});
// Every step - model calls, tool calls, agent decisions - arrives
// in AttestAI sealed, with token counts.The SDK's built-in telemetry does all the span work - we tested this against live gpt-5.5 with tools and multi-step loops.
Python
Python agents - OpenAI SDK, LangChain, or hand-rolled.
# pip install opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(
endpoint="https://YOUR_HOST/v1/otlp/<projectId>/v1/traces",
headers={"authorization": "Bearer atst_YOUR_KEY"},
)))
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("claims-agent")
with tracer.start_as_current_span("chat") as span:
span.set_attribute("gen_ai.operation.name", "chat")
span.set_attribute("gen_ai.request.model", "gpt-5.5")
# ... call your model as usualStandard OpenTelemetry, no AttestAI-specific package required.
LangChain.js
You want per-callback control instead of spans.
import { AttestClient } from "@attest/sdk";
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
const attest = new AttestClient({ baseUrl, apiKey, projectId, systemId: "claims-agent" });
class AttestHandler extends BaseCallbackHandler {
name = "attest";
async handleLLMEnd(output, runId) {
attest.log({ type: "model.call", sessionId, payload: { runId, usage: output.llmOutput?.tokenUsage } });
}
async handleToolEnd(output, runId) {
attest.log({ type: "tool.call", sessionId, payload: { runId } });
}
}
// pass the handler in callbacks: [...] - nothing else changesA callback handler bridges LangChain events to the SDK - useful when you also want human.approval and incident.flagged events.
Native SDK
Maximum evidence: oversight, disclosures, incidents.
# set once in your agent's environment:
# ATTEST_API_KEY=atst_... ATTEST_PROJECT_ID=... ATTEST_BASE_URL=https://YOUR_HOST
import { attestFromEnv } from "@attest/sdk";
const attest = attestFromEnv(); // that's the whole setup
// already on OpenTelemetry? one line instead:
// new NodeSDK({ traceExporter: new AttestSpanExporter() })
const run = attest.session(crypto.randomUUID(), "claims-agent");
run.start({ task: "review claim CLM-1003" });
run.modelCall("gpt-5.5", { usage: { input: 91, output: 24 } });
run.toolCall("policy_lookup", { policy: "P-88341" });
run.decision({ payout: 12400, currency: "EUR" });
run.humanOverride("m.reeves", { action: "approved" });
run.disclosureShown({ channel: "chat-banner" });
run.end({ outcome: "settled" });
await attest.flush();
// batched + serialized + retried; every batch carries an idempotency
// key, so a retry after a lost response can never double-recordThe typed taxonomy (17 event types) is where compliance evidence gets richer than debugging traces: disclosure.shown, human.override, incident.flagged.
Verification and evidence
The point of the ledger is that anyone can check it.
# Prove the record whenever you like:
curl -H "authorization: Bearer atst_YOUR_KEY" \
https://YOUR_HOST/v1/projects/<projectId>/verify
# -> { "valid": true, "checkedEvents": 184209, "headHash": "..." }
# Evidence pack for an audit or questionnaire:
curl -X POST -H "authorization: Bearer atst_YOUR_KEY" \
-H "content-type: application/json" \
-d '{"framework":"eu-ai-act-art12-19","periodStart":"...","periodEnd":"..."}' \
https://YOUR_HOST/v1/projects/<projectId>/packs
# Everything is exportable - stream the full ledger, hash columns included,
# for independent re-verification or your own warehouse:
curl -H "authorization: Bearer atst_YOUR_KEY" \
"https://YOUR_HOST/v1/projects/<projectId>/export?format=jsonl" # or csvEnterprise deployment
Self-serve today, EU-dedicated or self-hosted at scale.
The pilot runs against our hosted EU endpoint with your own org, keys and retention policy. Enterprise plans add dedicated EU infrastructure or a self-hosted build inside your own VPC, DPA included - the ledger design is identical either way.