Documentation

Logs in. Signed evidence out. Every endpoint, documented.

kolm audits your AI application: the agent and every tool, endpoint, identity and data flow around it, from the logs you already have. These docs cover the whole API surface: send telemetry to /v1/audit, read the signed envelope, and let your reviewer verify it offline against your key.

Real endpoints, real field names One canonicalization, everywhere Apache-2.0 verifier
Step 01

Send the logs

One curl to POST /v1/audit/scan with the telemetry your application already emits. Go to quickstart

Step 02

Read the envelope

One canonical JSON object: findings, evidence digest, passport, and the Ed25519 signature over the exact bytes. Go to the envelope

Step 03

Verify it offline

Your reviewer checks the signature in the browser, in a script, or against the API. Go to verify

AUDIT PIPELINE / LOGS -> SIGNATURElive
Input

App telemetry

The observability your application already emits, imported or captured at the gateway.

importor capture
Engine

Audit run

Permissions, audit trail, egress and injection graded against scope.

ASR-01.. ASR-08
Output

Signed report

One canonical object, sealed over the exact bytes: same logs in, same signature out.

Ed25519envelope
Verify

Reviewer checks

Offline against the key inside the report. The run is reproducible end to end.

VALIDoffline
PIPELINE same logs in, same signature out reproducible
01 / Quickstart

Run the free scan in three steps.

The scan reads your application's existing logs and returns a watermarked, signed preview of the full report. It is the same engine and the same signature as the paid deliverable; only the tier differs.

Step 1

Get a key

Create an account at /signup. Every request authenticates with Authorization: Bearer ks_... The scan is free.

Step 2

Post the logs

POST /v1/audit/scan accepts JSONL text, a JSON array, or an array of records in the logs field, up to 20,000 records.

Step 3

Read the result

The response carries a summary, the evidence grade, and the full signed envelope. Paste the envelope into /verify to see the check pass.

AUDIT LIFECYCLE / SEND -> VERIFYlive
Send

POST your logs

One call to /v1/audit/scan with the telemetry you already emit.

1 curl, free tier
Assess

Controls run

ASR-01 through ASR-08 grade against scope. Unassessed controls are named, not guessed.

8 controls graded
Sign

Envelope sealed

The report is canonicalized and signed with Ed25519 over the exact bytes.

Ed25519 canonical bytes
Verify

Reviewer checks

Offline in the browser, in a script, or against the public API.

VALID no account needed
READOUT 1 curl, 8 controls graded, Ed25519 sealed verified offline

The run is reproducible (same input, same output)A run is reproducible when the same logs always produce the same report and the same signature, so a reviewer can re-run the check and get the identical result.: the same logs in produce the same signature out.

curl -s https://kolm.ai/v1/audit/scan \
  -H "Authorization: Bearer ks_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "subject": "checkout-assistant",
    "logs": [
      { "model": "openai/gpt-4o",
        "user": "agent-checkout",
        "timestamp": "2026-06-11T09:14:02Z",
        "request": {
          "messages": [{ "role": "user",
            "content": "refund order 1042" }],
          "tools": [{ "type": "function",
            "function": { "name": "issue_refund" } }]
        },
        "response": { "choices": [{ "message": {
          "role": "assistant",
          "tool_calls": [{ "function": {
            "name": "issue_refund",
            "arguments": "{\"order\":1042}" } }]
        } }] }
      }
    ]
  }'
{
  "ok": true,
  "id": "audses_9c81b2f0e6a44d1c77aa",
  "report_id": "asrr_mbqv3k",
  "signed": true,
  "key_fingerprint": "...",
  "summary": {
    "readiness_pct": 64,
    "total_findings": 3,
    "by_severity": { "high": 1, "medium": 1, "low": 1 },
    "blocking_count": 1,
    "tamper_evident": false
  },
  "ingest": { "records": 1, "events": 2 },
  "evidence_tier": { "grade": "B" },
  "report": { "schema": "kolm-audit-report-1", "...": "..." },
  "verify_url": "https://kolm.ai/verify"
}

No hash chain in your logs yet? Drop in the evidence-grade logging shim, a zero-dependency logger for Node or Python that emits the canonical event shape with per-agent keys, declared grants and a SHA-256 hash / prev_hash chain. Feed its toJSONL() / to_jsonl() output to the scan and the run grades the evidence tier B (hash-verified). No network call leaves your runtime; see the shim readme.

Optional body fields: subject (what the report names, 200 chars), source (a label for where the logs came from, 64 chars), retention_days (your declared log-retention window), allowed_hosts (your sanctioned egress destinations, up to 200 hostnames; see ASR-3), coverage_declaration (what window and systems the export covers; bound inside the signed envelope, see the envelope), sign and persist (both default true). The Signed Readiness Report is $750 one-time: POST /v1/audit/report/checkout with {"audit_id":"audses_..."} re-signs the same audit without the watermark.

02 / Log formats and connectors

The telemetry kolm reads.

The engine ingests the observability your application already produces. Connector detection sniffs the export shape, most-specific markers first; you can also tag the source yourself. Unparseable lines are skipped at the boundary and reported, never fatal.

SourceWhat the detector keys on
OpenTelemetryOTLP exports: resourceSpans / scopeSpans, span attributes under gen_ai.*, http.*, url.*.
OpenInferenceOTLP spans carrying openinference.span.kind, llm.model_name, tool.parameters, retrieval.documents. Checked before plain OTel.
DatadogLLM Observability spans: meta.kind of llm / tool / agent / workflow / task / embedding / retrieval, ml_app, start_ns.
Trace observation exportsTraces with observations[] of type GENERATION / SPAN / EVENT, or bare observations with traceId / modelParameters / usage.
Run tree exportsRuns with run_type, child_runs, dotted_order, or provider metadata such as model name and token usage.
MCP server logsJSON-RPC 2.0 rows: tools/call requests paired with results by id, tools/list declared tool surfaces, initialize serverInfo, and kolm mcp-gateway receipts (mcp-tool-call-1).
OpenAI Agents SDKTrace exports: object of trace / trace.span with span_data.type of agent, generation, function, handoff or guardrail. Handoffs land as explicit delegation edges.
Gateway logsLiteLLM, Helicone, Portkey and OpenRouter rows read natively: request / request_body, response / response_body, api_base, provider-prefixed model names.
Plain JSONLOne JSON object per line. Also accepted: a JSON array, a wrapper object (data / rows / events / generations), or a single record. OpenAI chat, Responses and Assistants envelopes and Anthropic tool_use blocks are all recognized.

Running LangGraph, CrewAI or the Vercel AI SDK? Each ships an OpenTelemetry exporter (LangGraph and CrewAI also via OpenInference instrumentation; the Vercel AI SDK via its built-in experimental_telemetry OTel support). Point the exporter at a file or collector and the export arrives through the OpenTelemetry or OpenInference connector above. No kolm-specific code in your application.

The fields the analyzers use.

Seven analyzers (permission, audit trail, agent identity, delegation, model provenance, retrieval and memory, data egress) plus the injection battery read the same normalized events. The dimensions that matter, per record: tools granted (request.tools / functions), tools invoked (assistant tool_calls / function_call), the egress host (api_base, the provider prefix on model, URL-bearing tool arguments like url / webhook / to), the identity behind the call (user, metadata, key id), timestamps (ISO or unix epoch), tamper evidence (hash / prev_hash chains), and message and tool-argument content for the sensitivity scan: PII classes plus secret-shaped tokens, so an API key in a tool-call body flips has_sensitive the same way an SSN does. A record can be minimal; absent dimensions are reported as not assessed, never guessed.

terms are defined in the glossary

{"model": "openai/gpt-4o",
 "user": "agent-checkout",
 "timestamp": 1781082842,
 "api_base": "https://api.openai.com/v1",
 "request": { "messages": [...],
   "tools": [{ "type": "function",
     "function": { "name": "send_email" } }] },
 "response": { "choices": [{ "message": {
   "tool_calls": [{ "function": {
     "name": "send_email",
     "arguments": "{\"to\":\"ops@acme.com\"}"
   } }] } }] },
 "hash": "f3ab...", "prev_hash": "09cd..."}
03 / Sessions API

Sessions, for exports bigger than one request.

A single request body caps at 4 MB. Sessions accumulate logs across calls, up to 24 MiB and 20,000 records per session, then run the audit once over the whole export. Each session belongs to your tenant and never crosses it.

EndpointWhat it does
POST /v1/audit/sessionsOpen a session. Body: { subject?, source?, retention_days? }. Returns 201 with an audses_* id.
POST /v1/audit/sessions/:id/ingestAppend logs. Body: { logs }. Repeat per chunk; returns accepted and the running record_count.
POST /v1/audit/sessions/:id/runRun the audit and sign. Body: { subject?, source?, retention_days?, allowed_hosts?, coverage_declaration?, sign? }. Closes the session.
GET /v1/audit/sessions/:idStatus and summary. Raw logs are never echoed back.
GET /v1/audit/sessions/:id/reportThe deliverable: ?format=json|html|pdf. JSON is the bare signed envelope, downloadable and offline-verifiable.
GET /v1/audit/sessions/:id/exportProcurement artifacts over the same signed envelope: ?format=csv|xlsx|drata|vanta|exec|crosswalk|sarif|oscal|aibom|scorecard|modelcard.
POST /v1/audit/sessions/:id/deltaDiff two reports you own: ?against=<audses_* or asrr_*>. Readiness movement, control transitions, finding counts.
GET /v1/audit/sessions/:id/questionnaireA standard security questionnaire pre-filled from the signed report: ?template=generic-ai-vendor&format=csv. Unassessed controls answer n/a, never an unsupported yes.
GET /v1/audit/reportsEvery audit and report your tenant owns; powers the dashboard.
POST /v1/audit/importPull logs from a URL you control: { source:"url", url, headers? }, same caps, same optional allowed_hosts and coverage_declaration, same signed result. Built for a scheduled sidecar.

The source tag kolm-capture is reserved. Sending {"source":"kolm-capture"} with no logs audits the gateway captures stored for your tenant and grades the evidence A (first-party capture); supplying it with vendor logs is a clean 400, so imported logs can never claim capture-grade evidence.

04 / The controls

What the audit assesses, ASR-1 to ASR-8.

Every finding in a report traces to one of these controls, and each control maps to the frameworks your reviewer cites: SOC 2, ISO 42001, NIST AI RMF, the EU AI Act, OWASP and MITRE ATLAS. kolm maps findings to those standards; it does not certify against them.

ControlWhat it requires
ASR-1 Least privilegeScopes held match scopes used; no shared keys across isolation boundaries.
ASR-2 Audit trailAppend-only, tamper-evident activity log with a stated retention policy.
ASR-3 Data egressEgress destinations enumerated; sensitive fields redacted before they leave.
ASR-4 InjectionDirect and indirect injection and jailbreaks tested and reported with reproductions.
ASR-5 ProvenanceModel and dependency provenance; MCP and vendor surface enumerated.
ASR-6 EvidenceFindings signed, logged, and offline-verifiable.
ASR-7 Memory and retrieval integrityRetrieval sources enumerated and trusted; memory writes carry an integrity link and a recorded author.
ASR-8 Multi-agent delegationEach handoff is attributable and attenuates the sub-agent to a subset of the delegating agent's authority.

ASR-3 has a dedicated egress analyzer. Every destination host is inventoried with call counts, tools and sensitivity flags, then graded against the allowed_hosts you declare (acme.com admits its subdomains). A destination outside the allowlist is a high finding; a secret-shaped token leaving the boundary is critical; with no allowlist declared, the enumerated-but-unvetted surface is a medium finding. A window with no observed egress marks the control untested and drops it from the readiness denominator, never a silent pass.

ASR-4 results carry an evidence_source per probe. Passive outcomes are scored from observed traffic. The consented active battery behind the Deep Red-Team add-on (+$10,000) sends the same probe corpus, from a fixed seed, against a staging endpoint you name in a written consent record, and merges live outcomes into the same red_team block. Worst outcome wins on merge: an exposed result is never erased.

A control the run could not assess is listed under summary.not_assessed with a reason, and the readiness number covers assessed controls only. The full crosswalk, pillar by pillar, lives at what we test.

05 / The report envelope

One object. The signature covers it.

A report is one canonical JSON object, schema kolm-audit-report-1. The Ed25519 signature covers the recursive, key-sorted, whitespace-free serialization of every field except four: the signature block itself, and the detached evidence attached after signing.

schemakolm-audit-report-1
report_idasrr_mbqv3k
tier · watermarkscan · true free preview
evidence_tiergrade A / B / C signed
subjectname · source · records · events
coverage_declarationwindow · systems · attestor optional, signed
summaryassessed controls only
findings / frameworksseverity-sorted, framework-mapped
evidence_digestsha256 over the analyzed events
passport · red_teamidentity graph · injection results
public_keyed25519 · embedded PEM
key_fingerprint410302c93becdcc3...
signatureed25519:0XCoqRkb... covers the canonical bytes
log_checkpointMerkle inclusion path embedded, detached

Signed, or detached. Nothing in between.

Signed and tamper-evident: tier, watermark, evidence_tier, the subject, the summary, every finding, the remediation roadmap, the caveats, the evidence digest and the passport. The free scan signs tier:"scan" with the watermark on; the $750 purchase re-signs the same audit as tier:"report". Because both fields sit inside the signed payload, stripping the watermark breaks the signature.

The caveats are bounded claims, signed with everything else. They name the exact PII classes and secret shapes the sensitivity scan covered, so a clean egress posture is never read wider than the detectors. A coverage_declaration (window, systems, attestor, optional vendor Ed25519 signature) binds the vendor's statement of what the export covers next to evidence_tier; when the observed event span disagrees with the declared window, or vendor-supplied logs arrive with no declaration at all, the signed caveats say so.

Excluded from the signed bytes: signature_ed25519 (a signature cannot cover itself) and the detached evidence (log_checkpoint, timestamp_evidence, co_signatures), each of which references the sha256 of the signed canonical payload. The checkpoint embeds the report's RFC 9162 Merkle audit path (log_checkpoint.inclusion: leaf index, tree size, path hashes) at signing time, so transparency-log inclusion verifies offline along with the signature. The key fingerprint is the SHA-256 of the SPKI DER public key, truncated to 128 bits. The full byte-level rules are in the spec; the field-by-field walk is at anatomy of a report.

Scope is contractual. Permission posture, redaction and audit-trail integrity are assessed. Injection is tested and reported, not warranted.

06 / Verify a report

Three ways to check the same signature.

The first two run offline. No account, no upload, no kolm server in the trust path: the report embeds the public key it was signed with, and the canonicalization is byte-identical everywhere, so any edit is self-evident.

Browser

Paste it at /verify

Drop the report JSON on the page. The Ed25519 check runs with WebCrypto, in front of the reviewer, against the published issuer keyring.

Open the verifier

Script

kolm-audit-verify.js

A dependency-free module that runs in browsers and Node 20+. It returns a structured result and never throws; exit on a falsy ok to gate a pipeline.

read the source · apache-2.0

API

POST the envelope

POST /v1/audit/report/verify is public and adds the issuer and revocation checks. It consults a kolm server, so treat it as the convenience path, not the trust path.

120 requests/min per address

// node 20+ (global WebCrypto) or any modern browser
import { verifyAuditReport }
  from './kolm-audit-verify.js';

const report = JSON.parse(
  fs.readFileSync('asrr_mbqv3k.json', 'utf8'));
const res = await verifyAuditReport(report);

// res = { ok, reason?, key_fingerprint,
//         checks: [{ name, ok, detail }] }
curl -s https://kolm.ai/v1/audit/report/verify \
  -H "Content-Type: application/json" \
  -d @asrr_mbqv3k.json

{ "ok": true,
  "trusted": true,
  "verify":     { "ok": true },
  "issuer":     { "recognized": true, "kid": "live" },
  "revocation": { "status": "live" } }

A consumer that checks only the signature would accept a forgery re-signed with a rogue key: check trusted, which requires the signature to verify, the embedded key to be a published kolm issuer, and that key to be unrevoked. To pin the issuer yourself, fetch GET /v1/audit/issuer-key and confirm a key's lifecycle at GET /v1/audit/issuer-key/:fp/status (live, rotated or revoked).

Inclusion verifies offline too: verifyInclusionOffline in the same module walks the Merkle audit path embedded at log_checkpoint.inclusion with WebCrypto SHA-256 and confirms the report's leaf sits under the checkpointed root. A checkpoint that predates embedded proofs returns no_embedded_proof; fall back to GET /v1/transparency-log/proof/:seq.

07 / Continuous re-attestation

The Trust link that stays current.

Continuous is $299/$999 per month and re-signs your report on a schedule and on every deploy, behind one stable, unguessable URL your buyer keeps. The link states its own freshness: a current report shows when it last re-attested, a report older than 8 days carries a stale banner, and a lapsed subscription says so while staying verifiable.

EndpointWhat it does
POST /v1/audit/continuous/checkoutSubscribe: { "plan": "starter" | "growth" }.
POST /v1/audit/continuous/deploy-hookForce an immediate re-attestation; call it from CI after a deploy (your own API key authenticates it).
GET /v1/trust/:slugThe shareable report, public to whoever holds the slug: ?format=html|json|pdf.
GET /v1/trust/:slug/exportThe same procurement formats as the session export, straight into a buyer's GRC tool, no account.
GET /v1/trust/:slug/badge.svgAn embeddable readiness pill for a README or status page. It goes grey after 30 days without a refresh, and on issuer-key revocation. See the badge page.
GET /v1/trust/:slug/questionnaireThe pre-filled questionnaire, derived from the signed report behind the link.
GET /v1/trust/:slug/deltaThe drift between the report the link serves now and the prior signed report. A first attestation returns delta:null with a note.

To gate pull requests on the delta instead, the kolm-agent-audit GitHub Action scans on every PR, diffs against your prior signed report, and upserts one PR comment; the reference lives in docs/continuous-ci.md.

08 / Errors and limits

Every failure is a JSON envelope.

Nothing on this surface throws an HTML error page. A failed call returns { "ok": false, "error": "...", "detail": "..." } with the status codes below, and an unmatched path under /v1/audit or /v1/trust returns a JSON 404.

Status · errorMeaning
401 auth_requiredMissing or invalid key. Send Authorization: Bearer ks_...
400 logs_required · no_recordsThe body had no logs field, or nothing in it parsed as a record.
400 source_reservedThe kolm-capture source tag is reserved for the gateway-capture bridge.
400 invalid_allowed_hosts · invalid_coverage_declarationallowed_hosts takes at most 200 hostname strings, each 253 chars or fewer; a malformed coverage declaration is rejected with the failing field named in detail. A declaration carrying an invalid vendor signature is a hard reject.
404 session_not_found · not_foundNo such session, report or Trust link for this tenant. A foreign id reads the same as an absent one.
409 session_closed · report_not_readyA session that already produced its report stays closed; a report endpoint called before run says so.
413 too_many_records · session_too_largeCaps: 20,000 records and 24 MiB of JSONL per session or scan; 4 MB per request body. Split the export across ingest calls or sessions.
422 audit_failedThe records parsed but could not be analyzed.
429 rate_limitedThe public verify endpoint caps at 120 requests per minute per address. Verify offline instead.
503 no_signer_configured · pdf_unavailableThe server cannot sign or render right now; the audit itself is unaffected. Retry or mail dev@kolm.ai.

That is the whole surface: logs in at /v1/audit, a signed envelope out, and a check your reviewer can run without us. The byte-level format lives in the spec; questions reach dev@kolm.ai.

Verify the sample, then send your own logs.

Open the verifier on a signed sample, then run the free scan on your application's telemetry and hand your buyer a report they can check in their own browser.

Ed25519-signed Offline-verifiable Sample report

Caveats: Scope is contractual. Permission posture, redaction and audit-trail integrity are assessed. Injection is tested and reported, not warranted.