Skip to main content
@checkfu/sdk is the TypeScript client for the Checkfu API. Its typed surface is generated from the same OpenAPI document that publishes these docs. The generated files are checked in, and the drift gate pnpm test:sdk-drift compares a stamp of (spec, generator) and a content hash of each generated file, failing when they no longer match. That gate runs at commit time whenever a change stages the spec, the generator, or a generated file; on the repository’s check:heavy cadence; before every release; and again at publish — so a released client cannot lag the API it was generated from. It does not run on every landing, so main between cadence passes is the one place a freshly landed API change can briefly lead the checked-in client. Only the transport core (auth, retry, idempotency, pagination, SSE) is hand-written. Plain HTTP remains the baseline integration; the SDK is a convenience, never a second contract.
Private alpha: the package is not yet published to npm. Today it lives inside the platform repository while the public release is prepared. The import surface below is the stable one.
The generated checkfu.beta.agents Agent and Version surface is current. The Session and tools.sync examples below are migration-fenced until they admit CMA Agents, Environments, and Resources directly; do not combine those legacy examples with a newly created CMA Agent yet.

Quick start

baseUrl defaults to https://api.checkfu.com. Omitting an option falls back to CHECKFU_API_KEY or CHECKFU_BASE_URL. The API key is bound to one Workspace, matching CMA; the client accepts no per-request Workspace selector. The client sends your API key to whatever baseUrl names, so the endpoint is validated once at construction, before any request: HTTPS is required except for exact loopback HTTP (localhost, 127.0.0.1, [::1]), and credentials, a query string, or a fragment in the URL are refused. A proxy path is preserved as given and trailing slashes are removed. A refused value throws CheckfuError without echoing the value, which may itself be a secret. To render best-effort live output before settlement, opt in explicitly:
The opt-in widens the generator type with SessionEventPreview. Preview frames are not durable and do not move the SDK’s reconnect cursor. If the corresponding observation commits, its final persisted event carries the same event ID and remains authoritative. A transparent reconnect yields a preview_reset item before later frames so the consumer can discard every unmatched preview.

What the client handles for you

  • Headers: Authorization and Checkfu-Version per the authentication contract, on every versioned call.
  • Idempotency: mutating calls accept an idempotency key and retries replay rather than duplicate, per the HTTP overview.
  • Pagination: list surfaces follow the opaque next_page cursor for you.
  • SSE resume: events.stream reconnects with Last-Event-ID, advances its cursor only for strictly increasing durable frames, and rejects duplicate or reordered sequences. A connectTimeout (default: the client’s request timeout) bounds header establishment; retryable handshakes (429/>=500), connect timeouts, mid-stream drops, and clean closes share one maxReconnects budget and honor parsed Retry-After. Auth, policy, and protocol failures never reconnect. Optional live previews are cursor-neutral and reset explicitly after reconnect. An unreadable retention/ZDR range raises EventStreamCursorGapError instead of being skipped. It implements the discipline from Resume a stream.
  • Custom tools: defineTool declares and validates one customer-executed tool, tools.sync updates only the AgentDefinition draft and its enforced approval ActionPolicies, and tools.serve implements the ToolServing contract over the public event log for explicitly named Sessions.
  • Session projections: createSessionProjections() and the individual projectors (MessageProjector, ToolCallProjector, InteractionProjector, MultiagentProjector, ContextLedgerProjector) fold the one event stream into the named read-side views — messages, tool calls, ActionApprovals and Questions, the multi-agent view, and the context ledger. They are pure, replay-idempotent, process-lifetime folds with no transport or durable snapshot format of their own. The observer’s eventDelta is retryable input, not a complete view; an external provider owns cumulative state and its cursor in one transaction. See Project a session.
The bounded hand-written core also contains surfaces that OpenAPI cannot generate. The two SSE helpers are sessions.events.stream(sessionId) and CMA-shaped sessions.threads.events.stream(threadId, { session_id }); both resume with Last-Event-ID, and the thread helper tails that thread’s assigned projection: condensed Session history for the primary or complete local history for a child. Add event_deltas: ["agent.message"] to the thread params to opt into previews. sessions.exportStream hands back bytes instead of a parsed document. capabilities.status(id) is a convenience over GET /v1/support/capabilities. connections.waitForActive(id, { timeoutMs }) polls the public read until a pending Connection becomes active and healthy — there is no connection-lifecycle webhook, so this is the honest completion signal for the interactive connect flow (see Connect a provider account). Its timeoutMs is one overall budget covering every poll, response body, transport retry/backoff, and sleep; a caller signal abort rejects with a ConnectionError carrying the caller’s reason. The schema adapter and ToolServing projection behind defineTool and tools stay dependency-free and use only generated public operations.

Driving a session: the six sendable events

events.send takes one discriminated event. The six drive events are user.message, user.interrupt, user.custom_tool_result, user.tool_confirmation, user.question_answer, and user.define_outcome, each with a payload that carries required attribution (authored_by, caused_by). The hand-written interaction helpers submitCustomToolResult, respondToToolConfirmation, allowTool, and denyTool send the matching event through the primary Session. Pass sessionThreadId for a child action; the helper echoes it with toolUseId and required actor attribution. The separate checkfu.actionApprovals.decide(id, …) resource method remains available for ActionApproval queues and optimistic-concurrency workflows.

Downloading an export as bytes

checkfu.sessions.export(id) gives you the parsed export document. For a log too large to hold in memory, checkfu.sessions.exportStream(id) resolves to a ReadableStream<Uint8Array> of the same checkfu.session-export v1 response, without buffering the complete JSON:
You own the returned stream: read it to EOF or cancel it. The request’s timeout and abort ownership stay live until you do, and cancelling propagates to the export request itself. Export and own your logs pipes it to a file.

Buffered response limits

The SDK incrementally collects ordinary JSON and binary success responses up to 64 MiB and error responses up to 64 KiB. Overflow raises a non-retryable ResponseBodyTooLargeError carrying the HTTP status and maximumBytes; response bytes are never attached to that error. There are exactly two compatibility exceptions for dated complete-export contracts. checkfu.memory.export(id) and the structured checkfu.sessions.export(id) method may buffer their complete aggregate without a client byte ceiling. Use checkfu.memory.exportPage(id, query) for bounded Memory traversal and checkfu.sessions.exportStream(id) for a large Session log. /export-pages, binary calls, different methods, similarly named paths, and every ordinary resource remain bounded.

Naming conventions

Resources follow create / retrieve / list / update / delete, and every API family has a generated group: sessions (+ .events, .checkpoints, .threads, .ledger, .mounts, .watches), agents, principals, connections, skills, memory (+ .documents, .revisions, .proposals, .changes, .curationRuns, .redactions, .workingRefs), automations (+ .firings, .deliveries, .reports, .sources), actionApprovals, workspaces, projects, files, budgets, usage, webhooks, audit, dreams, runs, skillProposals, instructionProposals, and the rest of the catalog. If it’s in the API reference, it’s on the client. Two shape rules that trip up newcomers from other agent platforms:
  • sessions.create requires a principal: the identity the session acts for, created with checkfu.principals.create(...) (Get access). The harness, model, and sandbox come from the Agent: the create shape technically lists those fields, but sending any value is rejected, never ignored. They are derived, not overridable.
  • Attribution is required on every drive event: authored_by names the Principal speaking, and each author is authorization-checked per event.

Next steps

Quickstart

The same first-session flow over plain HTTP.

Events reference

Every event the stream can yield, with payload shapes.