Skip to main content
Agent creation and tool synchronization now use checkfu.beta.agents, but this guide’s Session launch remains migration-fenced until the CMA-compatible Session surface lands. Do not use its Session example with a newly created CMA Agent yet.
The runnable examples/support-desk demo starts as a plain support inbox: Hono on Node, Vite and React, JSON fixture data, its own cookie session. Adding a Checkfu agent replaces none of that. This is the brownfield path. Your backend keeps its framework, data model, authentication, and application functions. Checkfu supplies the governed Session, the tool-serving SDK, the durable event log, and the React pieces you compose yourself.

Server boundary

Declare Custom tools over your existing functions and serve explicit Sessions from your backend worker.

React boundary

Proxy the event log through your backend and compose transcript, tool, status, and ActionApproval components.

What the finished journey proves

  1. The agent and the ticket HTTP routes call the same application functions.
  2. An agent-authored draft appears in the open ticket without a reload.
  3. Human and agent writes produce the same domain effect with different attribution.
  4. close_ticket parks on a real Checkfu ActionApproval rendered inside the support app.
  5. The selected ticket is supplied in the drive message; the agent does not scrape browser state.
The example’s boundary test permits only @checkfu/sdk and @checkfu/ui imports from Checkfu. It rejects internal packages and platform application imports.

Start from the host’s functions

The plain app already owns lookupTicket, draftReply, and closeTicket. Each function accepts the operation input used by both surfaces. An optional serving context distinguishes an agent call from a direct human call for attribution; it does not fork the business behavior.
The existing Hono reply route calls that function directly:

Declare the same functions as tools

defineTool wraps those exact function references with a name, a description, and an input schema. It does not move execution into Checkfu.
needsActionApproval maps to Checkfu ActionPolicy authority on use_tool:close_ticket. It is not a client-side confirmation flag. Sync updates the Agent and its SDK-managed ActionPolicy; the changed configuration automatically becomes the next immutable Version.
sync returns the current Agent. When the custom-tool set changes, the update automatically creates the next Version. An unchanged tool set is a no-op. See Serve tools from your app for validation errors, replay convergence, and approval sequencing.

Create a Session with visible context

When a teammate delegates from an open case, the server creates a normal Session and starts serving that explicit Session:
The v1 user.message shape has free-text content and attribution but no structured host-context field. The demo therefore prepends a documented, bounded in-band block:
The example README records a typed, bounded context field as a candidate product desire. The demo neither invents a private field nor lets the agent guess which ticket is open.

Keep credentials behind the existing backend

The browser never receives a Checkfu Workspace key. The Hono server exposes only the event page and stream routes for an authenticated, authorized Session, attaching its key upstream:
This is the proxy recipe from Embed a Session view, consumed in a non-Checkfu server stack. Authenticate the caller and verify its right to observe the requested Session before invoking the proxy. Never expose a generic credentialed passthrough.

Project the log into React

The host binds useSessionEvents to its proxy with the Workspace-proxy transport. The hook consumes the stream as data — durable events append by seq, previews replace themselves, gaps get one bounded backfill — and the host’s fetch keeps auth (cookies, sign-in retries) entirely host-owned.
When the highest event sequence changes, the support app refetches the open ticket from its own API. A served draft_reply therefore appears in the existing reply box without a reload. The ticket store remains application truth; the Session log is the durable truth that the agent action occurred and the live invalidation source. The right rail composes focused kit exports rather than adopting a Checkfu application shell:

Surface the consequential action

When the agent calls close_ticket, ActionPolicy emits action_approval.pending and the run waits. The backend lists the pending custom-tool ActionApproval for that Session and passes its frozen context to the kit:
Approve or deny goes to the host backend, which calls checkfu.actionApprovals.decide. On approval, ToolServing runs the same closeTicket function as the human button; on denial, the function never runs. Both decisions remain in the Session event log.

Run the complete example locally

Start the owned mock stack and use its protected connection file. The file contains the local URL, credential, Workspace, AgentDefinition, and Principal expected by the example.
Open http://127.0.0.1:4174, select TKT-1042, and choose Ask case agent. The visible default prompt uses the mock harness’s deterministic Use <tool> with <JSON> grammar, so the first Session drafts a reply through the host worker. When it completes, choose Start close task. That second Session calls close_ticket and parks on the frozen ActionApproval in the same right rail. The mock executes one explicitly named Custom tool per Session, which is what makes both steps repeatable; a hosted model picks its tools from ordinary prose instead. Pointing the same application code at hosted Checkfu changes only the server-side connection values.

Verify the customer boundary

The journey test scripts the Checkfu event log on one boundary and jsdom on the other. One case drives context in, parks and serves the Custom tool, projects the draft live, escalates the close, and ends on run.completed. The other reconciles a delayed ActionApproval and projects a denial without running the host action. A live smoke swaps those two test doubles for checkfu dev and a browser, and the application code does not change.