Skip to main content
Every log you create is exportable in a stable, self-describing format you can archive, replay, or take elsewhere. This guide covers the three export surfaces (the session log, the context ledger, and the audit trail) and what each one answers.

Export one session, completely

GET /v1/sessions/{id}/export returns the whole log as a single self-contained document. CHECKFU_API_KEY and CHECKFU_WORKSPACE_ID come from Get access; SESSION is the sess_… you are exporting.
The API sends that one JSON document incrementally: it freezes end_seq, reads bounded pages in order, and stops reading when the client disconnects. curl therefore writes large exports directly to disk without holding the complete history in memory. The TypeScript SDK exposes the same response as an owned byte stream:
Read the SDK stream to EOF or cancel it if you stop early; cancellation propagates to the export request. The Checkfu console uses the browser’s streaming file API when available. Browsers without it use a compatibility Blob capped at 32 MiB and show an error above that limit. There is no unbounded in-memory fallback. The response identifies itself so an offline reader needs no out-of-band context: Because envelopes keep their seq, id, and schema_version, an export is replayable: you can rebuild any projection from it offline exactly as the platform would, or diff two exports by end_seq. For incremental sync instead of snapshots, page GET /v1/sessions/{id}/events with a cursor. See Resume a stream. Erasure stays honest in exports: content expired, crypto-shredded, or omitted under zero-data-retention is absent, never silently reconstructed. Every owned sequence position is still accounted for: readable envelopes appear in events, while unavailable positions identify their seq, optional content class, and shredded or zdr reason. An explicit Session erasure is stronger and makes the export endpoint return 404.

Read an export offline

The session-export-inspector example opens one exported .json file directly from your machine with no Checkfu credential and no network connection. Drop the file you exported above onto the page: it parses the unknown JSON, decodes it as a SessionExport, and proves every sequence position is accounted for before rendering a readable transcript with the same @checkfu/ui kit the console uses. Unavailable positions stay explicit — grouped by reason, content class, and structural-replay status — and are never fabricated as readable events. The inspector validates schema validity and exact position coverage only. It does not claim event authenticity, audit-chain integrity, provider truth, or a full session replay, and structural_replay: incomplete stays visible. Files larger than 32 MiB are rejected before they are read, and malformed documents never reveal their contents in an error. Real exports may contain sensitive session content, so do not commit them; the example ships synthetic fixtures only.
This endpoint exports what happened, not what the agent knows. Memory stores are separate and export separately, through GET /v1/memory-stores/{id}/export-pages: a resumable, normalized graph stream of revisions, document versions, and refs. If you are answering “get all my data out”, you want both halves. See Memory.

Read the context ledger

GET /v1/sessions/{id}/ledger answers a different question: “what is loaded and what has it cost” rather than “what happened”. It is a rebuildable session-level view carrying the admitted definition version, current and cumulative context-token counts, the loaded capabilities (tools and skills by reference), and every compaction. Compactions come back as compactions, each with its event_id, seq, strategy, tokens_before, tokens_after, and created_at. When the platform has bounded the retained conversation itself, a second condensations array reports each of those: event_id, seq, the through_sequence it covered, how many user and agent messages it folded into an elision marker (condensed_user_messages and condensed_agent_messages), and created_at. condensations is absent rather than empty when nothing has been condensed, so read it defensively. The ledger deliberately contains no prompts, messages, or argument content, so it is shareable where transcripts are not. (For which mounts a specific Run received, read that Run’s session.mounts_resolved event instead.)

Export the audit trail

Governance actions (PermissionAssignment changes, approval responses with responder identity, policy decisions, credential access) live in an append-only audit log, queryable and exportable from day one:
after is a position cursor: feed the last record’s position back in to export incrementally. Audit records are append-only and survive subject erasure with the subject link destroyed. The fact that an action happened is permanent even when the actor’s identity is gone.

Verify the trail is untampered

Each record carries two store-assigned fields that make the trail tamper-evident rather than merely append-only: content_hash, a SHA-256 over the record’s checkfu.audit.v1 canonical immutable bytes, and prev_hash, the content_hash of the Workspace’s previous chained record (null at the chain’s genesis). Both appear in list responses and the SIEM export. verifyAuditChain and verifyAuditChainPage recompute every content hash as well as checking every predecessor link: content_hash_mismatch means immutable record content changed, while prev_hash_mismatch means the chain was broken or reordered. Their failure values also carry the failing broken_at_sequence and a stable reason, so a poller can record them without parsing exception text. Verify unfiltered pages in ascending sequence; a filtered export omits predecessors and is not independently chain-verifiable. sequence orders verifier input and detects duplicate or out-of-order pages, but it is deliberately excluded from the hashed bytes and may have gaps because its counter is global across Workspaces. Cryptographic adjacency—and therefore a skipped chained page—is detected by prev_hash, not by trusting the cursor column being audited. Pass the successful page’s serializable state to the next page, together with next_after. An empty page preserves that state. Records written before chaining was enabled carry null in both hash fields and are accepted only as an unhashed legacy prefix; their content and completeness cannot be proven retrospectively. A null content_hash after chaining begins is a failure. A valid result proves that the exported canonical records are mutually consistent. It does not prove that an actor’s narrated action was true, nor that a filtered or otherwise incomplete export contains every Workspace record.

Verify with the CLI

checkfu audit verify is the first-party command that walks an unfiltered ascending export through the canonical SDK verifier, so an operator does not have to write and secure their own poller:
The command always sends an unfiltered POST /v1/audit/export ascending by the endpoint’s contract. It offers no actor, action, resource, or time filters, because a filtered export omits predecessors and cannot prove chain completeness. With --state on POSIX, one renewable lock is held before the command reads or resets the checkpoint and remains held through the complete audit walk and every page publication. A competing verifier for the same directory waits at lock acquisition, so it cannot fetch from a stale prefix or publish an older valid cursor after the active owner. Lock acquisition is bounded to roughly 30 seconds; timeout occurs before checkpoint reads, writes, or audit requests. Stateful verification is unavailable on Windows until native owner-SID, ACL, reparse-point, and real-lane enforcement can uphold the same protection; omit --state there to run the unchanged stateless verifier. The verified prefix is checkpointed atomically after each successful page: the checkpoint is a mode-0600, current-user-owned regular file (never a symlink), bounded to 64 KiB, bound to the API origin and Workspace id, and written through an unpredictable same-directory temporary that is renamed over the prior file only after the new page verifies. Every export envelope’s Workspace id must agree with configuration, the checkpoint, its records, and adjacent pages. When configuration omits a Workspace, the first envelope establishes the durable binding, including for an empty export. A missing file starts a new chain; an existing file resumes at its cursor. --restart begins from zero but replaces the old checkpoint only after the first new page verifies, so a restart that fails early preserves it. Pass --page-size (1–1000, default 100) to lower the page weight for large detail records. On success the output carries only counts: pages and records processed, Workspace, last sequence, and chained versus legacy totals. On a broken chain it prints the stable reason and broken_at_sequence and exits 6 (invalid); transport, auth, and decode failures keep their existing exit classes and leave the last good checkpoint intact. The output never includes an audit record, actor, detail JSON, hash, or raw checkpoint — validity proves chain consistency, not narrated truth or provider completeness.
Audit action names are their own vocabulary (permission_assignment.created, api_key.revoked, session.deleted, …), a different namespace from event types, even where they describe related moments. To discover the values present in your Workspace, list unfiltered first and filter on what you see; the generated API reference documents the query parameters.

Answer the per-credential question

GET /v1/audit?connection_id=… answers the single most important vault question — who and which agent used one Connection, when, and for what — in one read. The CapabilityGateway attributes every connection-related fact to connection_id, including a pre-authorization denial it records against the Run (a ToolInvocation refused because the Connection was revoked), so a revoked credential’s last refused use is visible in the same filtered read. Each record carries the acting Principal, the requester, the tool, and the argument-hash in its detail_json — never custody material. The connection_id filter is a queryable projection outside the D91 hash chain, so it changes no content_hash byte and every export still verifies.

The three questions these answer

Exports carry no references a reader can’t resolve from the document itself, so they remain readable outside Checkfu. Day to day, the three endpoints map to the three compliance questions: “show me everything this agent did” (export), “what was loaded and what has it cost” (ledger), “who approved that” (audit).

Next steps

Resume a stream

Incremental paging over the same log the export snapshots.

Events reference

The envelope and catalog every exported event conforms to.