> ## Documentation Index
> Fetch the complete documentation index at: https://docs.checkfu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Let your users build agents

> Embed an Agent builder that calls canonical Checkfu resources without becoming another desired-state authority.

Your product can own the builder experience while Checkfu owns Agent identity, immutable AgentVersions, Environment resources, authorization, and Sessions.

The builder is a UI over the public contract. It is not a second Agent store, versioning system, policy authority, or runtime.

<Warning>
  The Checkfu CLI and TypeScript SDK are private-alpha artifacts with no self-service public installation channel.
  Part of this flow is behind a rollout fence; a deployment that has not enabled it refuses the request rather than running it.
  Confirm [capability status](/getting-started/status) before making availability part of your application's contract.
</Warning>

## Divide responsibility once

| Your product | Checkfu |
| - | - |
| Sign-in, tenancy, forms, drafts that have not been submitted, and product-specific UX | Canonical Agent and Environment resources, immutable AgentVersions, ownership claims, authorization, audit, and Session admission |
| Which builder documents a signed-in user may see or edit | Whether the resulting canonical mutation is valid and whether another active owner already claims the resource |
| Server-side custody of the Workspace-bound API key | Credential validation and canonical resource operations |

Never send a Workspace API key to the browser. Resolve the signed-in user and the builder document on your backend, then call Checkfu from that trusted server boundary.

## Use the generated SDK surface

The SDK request types come from the generated operation catalog. Accept those types at your boundary instead of maintaining a second hand-written Agent schema:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { Checkfu, agentEnvironmentUseAssignment, projectSessionProductEvent, type BodyOf, type CreateSessionBody, type Op, type RequestOptions, type SessionEvent } from "@checkfu/sdk"

const checkfu = new Checkfu({
  apiKey: process.env.CHECKFU_API_KEY,
})

type CreateAgentInput = BodyOf<Op<"agents.createAgent">>
type UpdateAgentInput = BodyOf<Op<"agents.updateAgent">>
type SaveRequestOptions = RequestOptions & { readonly idempotencyKey: string }

export async function createAgent(input: CreateAgentInput, options: SaveRequestOptions) {
  return checkfu.agents.create(input, options)
}

export async function updateAgent(
  agentId: string,
  input: UpdateAgentInput,
  options: SaveRequestOptions,
) {
  return checkfu.agents.update(agentId, input, options)
}
```

The exact generated namespaces are `checkfu.agents` and `checkfu.sessions`. Do not wrap them in a parallel compatibility client.

Your backend should associate one `idempotencyKey` with each logical create or save and retain the exact submitted body. If a response is lost, retry with that same key and body within the API's replay window. A later edit is a new save with a new key. Reusing a key with changed content conflicts; do not replace the key automatically to bypass that refusal.

The SDK keeps a mutation key stable across its own transport retries. Without an explicit key, a separate call creates a new one, so the SDK cannot connect that call to the earlier save. These helpers require the key and forward the SDK's other request options, including `signal`. Aborting a request does not undo a save the server already accepted; recover its result with the original key and body. Recheck the signed-in user's access to the builder document before every call, including a retry.

A material update advances the mutable Agent and creates the next immutable AgentVersion. Preserve the current `version` field when the generated update body uses it as the optimistic-concurrency guard. A stale browser tab should receive a conflict, reload the current Agent, and ask the person to merge intent. It should not overwrite a newer version silently.

## Create the Environment separately

Environment is a canonical Workspace resource, not an inline execution recipe hidden in the builder document. Create or select it through its own operation and store its ID beside your host object:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const environment = await checkfu.environments.create({
  name: "customer-preview",
})
```

A builder may let a user select models, tools, Skills, Files, Memory, or Connections, but each selection must compile into the current Agent and Environment contract. Do not expose provider-specific runtime coordinates or a harness-specific profile as the common Agent model.

## Start a preview Session

Grant the Agent use of the Environment once when the user connects them. Then
start a preview Session and send the preview prompt. The grant and the Session
use the same public operations as the quickstart:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
export async function allowPreviewEnvironment(agentId: string, environmentId: string) {
  return checkfu.permissionAssignments.create(
    agentEnvironmentUseAssignment(agentId, environmentId),
  )
}

export async function startPreviewSession(
  input: Omit<CreateSessionBody, "initial_events">,
  prompt: string,
  onEvent: (event: SessionEvent) => void,
) {
  const session = await checkfu.sessions.create(input)
  const inputEvent = await checkfu.sessions.events.send(session.id, {
    type: "user.message",
    payload: { content: prompt, caused_by: { kind: "api" } },
  })

  for await (const event of checkfu.sessions.events.stream(session.id, {
    lastEventId: inputEvent.seq,
  })) {
    onEvent(event)
    const productEvent = projectSessionProductEvent(event)
    if (
      productEvent.kind === "status" && (
        (productEvent.status === "idle" && productEvent.stopReason !== "requires_action") ||
        productEvent.status === "completed" ||
        productEvent.status === "failed" ||
        productEvent.status === "canceled"
      )
    ) break
  }
  return session
}
```

Pass the generated Session create body to the helper. To preview the saved
version rather than whichever version is current when admission runs, use
`agent: { type: "agent", id: agentId, version: savedAgent.version }` alongside
`environment_id`. Include `acted_as` when the selected Agent uses governed
Connections. Keep that Principal selection under your backend's authorization.
This helper sends the prompt as a separate event, so its input excludes
`initial_events`; use the Session create operation directly for that mode.

Session admission resolves and freezes the AgentVersion and Environment. The
message starts the preview turn; pass the observed events to the signed-in
user through your backend. The stream follows events after that input. An
idle boundary with `stop_reason: "requires_action"` needs a tool result or
approval before the turn settles. Editing the Agent later does not mutate that
Session. Start another Session to preview the next AgentVersion.

The create body accepts an optional `acted_as` naming a live Principal that holds `act_as` on the selected Agent. Admission checks that authority and freezes the Principal, and a Session whose Agent uses governed Connections requires it. The body never creates a Principal and does not accept author attribution. Do not restore retired attribution fields in a compatibility wrapper.

### Preview from the CLI

With the [private-alpha CLI artifact](/getting-started/cli-access), save the same
Session create body in `session.json`, including the exact saved `agent.version`
and any required `acted_as`. Then start and follow the preview:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
checkfu session chat "Preview this saved version." --input @session.json
```

The chat command sends its prompt as a separate event, so omit `initial_events`.
Use `session create --input @session.json --idempotency-key <caller-key>` when
creation must be recoverable across invocations, then chat with the returned
Session ID using `--session`. Keep the original body and key when retrying
creation; a changed saved version or Principal is a new intent. API admission
checks the selected Principal's authority for either path.

## Keep configuration changes coordinated

Choose the configuration source the builder edits and apply through its supported operations. A Save button does not create an ownership reservation.

### Direct Agent configuration

The backend may call the canonical Agent operation directly. The UI and SDK remain interfaces, not owners.

### Checked-in Agent project

If the builder edits a checked-in Agent project, apply it with
`checkfu agent apply` or the equivalent Agent create and update calls. Pass the
Agent ID so the directory stays bound to one Agent. Other resources, such as
Budgets, grants, Connections, Automations, and Memory, are created through their
own operations. No command applies them from a file.

### AutomationGraph configuration

Use the registered AutomationGraph revision and plan/apply lifecycle for graph
configuration. The resulting Automation still owns firing and the resulting
Session still owns execution.

### Resource conflicts

Inspect the exact typed error before retrying. A name match is not permission to
adopt or overwrite a resource, and a different interface does not bypass the
underlying operation's checks. See
[Authoring authority](/concepts/agent-blueprints#authoring-authority).

## Embed source-controlled authoring

A builder can also edit YAML or Git-backed source. In that mode:

1. save the proposed source change to the user's repository or review branch;
2. compile and plan it with the version-matched source compiler;
3. show the plan without treating it as a claim;
4. apply the reviewed Agent changes through canonical Agent operations; and
5. surface authorization, concurrency, or resource-policy errors to the user.

Do not run a perpetual reconciler in the browser or application server. Git is a desired-state source, not a second scheduler or runtime.

## Custom tools

`checkfu.tools.sync(agentId, tools)` invokes canonical Agent updates and the governed ActionPolicies required by `needsActionApproval`. Keep the declarations coordinated with the configuration source your builder edits, and handle the ordinary operation errors. It does not create a source-ownership or handoff lifecycle.

The serving loop stays in your application:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
await checkfu.tools.serve({
  sessions: [session.id],
  tools,
  signal: abortController.signal,
})
```

Checkfu supplies the Session event protocol and approval authority. Your application keeps the handler, business credentials, and business state.

## Verification checklist

Verify that the embedded journey proves all of these boundaries:

* the API key never reaches the browser;
* the backend rechecks host-object ownership for reads and writes;
* the same-key, same-body save recovers its result after a lost response, while changed-body reuse conflicts;
* stale Agent updates conflict rather than overwrite;
* an active source-owner conflict is shown and not retried through another interface;
* an existing Session keeps its frozen AgentVersion after an edit;
* a new Session admits the updated AgentVersion and selected Environment; and
* consequential Custom tools wait for the governed ActionApproval before the app-owned handler runs.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.