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

# Coming from Claude Managed Agents

> A term-by-term map from CMA resources, events, and headers to their Checkfu equivalents, a walked migration of CMA's own quickstart agent, and a generated list of what you give up today.

If you have built on Anthropic's Claude Managed Agents (CMA), Checkfu's shape will read as familiar on purpose: agents and sessions, a durable event log with `{domain}.{action}` event names, typed stop reasons, stream-only deltas, repo mounts, thin at-least-once webhooks, and an outbound-polling worker for self-hosted execution are all kept. The departures are deliberate too, and they cluster in two places: **governance** (who may do what is a resource you can list, audit, and revoke, never ambient configuration) and **portability** (the harness and model are choices, not the product's identity).

<Note>
  This page maps concepts. It is not a compatibility layer: Checkfu does not accept CMA request shapes, and there is no drop-in endpoint. CMA details below were re-checked against its live public documentation on 14 August 2026; since the 2 August check, CMA has split its memory beta headers, published per-component budget pricing (model list cost, web searches, and sandbox wall-clock), added an advisor entry to the multiagent roster spec, and added `injection_location` to vault credentials. CMA is a moving target and some of it is labelled beta or research preview; check its docs before relying on a CMA-side detail stated here.
</Note>

## The resource map

Every row carries the same one-word answer Checkfu's own plans and pull requests
must give when they depart from CMA, so you can tell a deliberate bet from an
accident at a glance:

* **`MATCH`** — CMA's shape, kept on purpose.
* **`CHOSEN (D…)`** — a different bet, with the decision number that records it.
* **`N/A`** — no CMA counterpart.

The CMA column cites the page and line in Anthropic's documentation each claim
was read from, so you can re-check any of it against the source.

| CMA (cited page:line)                                                                                                                                                        | Checkfu                                                                                                                                                              | Delta           | The difference that matters                                                                                                                                                                                                                                                                                                        |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Agent — one mutable config, `version` increments on update, and `agent_with_overrides` replaces model, system, tools or skills for a single session (`sessions.md:466`)      | [Agent](/concepts/agents) with a draft and immutable **published versions**                                                                                          | `CHOSEN (D108)` | A Session always runs a published version; drafts never leak into execution. Per-session variation is limited to the slots the release declares — model routing profile, sandbox profile, runner pool, and a **subset** of the release's own skills and tools                                                                      |
| Environment, passed as `environment_id` at session create, explicitly **not versioned** — "keep your own record of the changes" (`environments.md:563`)                      | **SandboxProfile**: base image, network egress, resource limits, retention                                                                                           | `CHOSEN (D262)` | The selection gesture maps directly, but Checkfu reserves Environment for deployment isolation and publishes immutable SandboxProfileRevisions, so the exact configuration a Session ran is always recoverable; see [below](#creating-a-session)                                                                                   |
| Vault and vault credentials, environment-scoped so "anyone with an API key for the same workspace can reference them" (`vaults.md:21`), max 20 credentials (`vaults.md:734`) | [Connection](/concepts/capabilities) owned by a Principal, plus a `use_connection` **PermissionAssignment**                                                          | `CHOSEN (D107)` | Credentials stay in custody and are owned by a Principal, not the workspace; the sandbox only ever holds a short-lived gateway capability                                                                                                                                                                                          |
| `permission_policy`, two values — `always_allow` executes with no confirmation, `always_ask` pauses the session (`permission-policies.md:20-21`)                             | Tri-state [ActionPolicies](/concepts/tenancy-and-governance) plus durable [ActionApprovals](/concepts/action-approvals)                                              | `CHOSEN (D6)`   | `deny` exists, and custom tools are governed too; see [below](#tool-permissions-and-approvals)                                                                                                                                                                                                                                     |
| `mcp_servers` on the agent — `type: "url"`, a name unique within the agent, a URL (`mcp-connector.md:232-234`) — with credentials matched per session by `vault_ids`         | ToolSources (`/v1/tool-sources`) resolved into Connections, each exposed to the harness as its own governed MCP server (`/v1/runtime/mcp/{connection_id}`)           | `CHOSEN (D64)`  | CMA declares servers on the agent and attaches credentials per session; Checkfu registers the server once per workspace and the agent names the discovered tools. Checkfu compiles the outbound call server-side, so per-tool authority is PermissionAssignments and ActionPolicies and no provider credential reaches the sandbox |
| Scheduled deployment — an agent starting sessions autonomously on a cron cadence (`scheduled-deployments.md:10`)                                                             | [Automation](/concepts/automations)                                                                                                                                  | `CHOSEN (D11)`  | Cron is one trigger kind among schedule, inbound webhook, and subscription; firings carry evidence and Reports                                                                                                                                                                                                                     |
| Memory store, capped at 100 kB per memory and 2,000 memories per store (`memory.md:209`), with immutable memory versions (`memory.md:915`)                                   | [MemoryStore](/concepts/memory)                                                                                                                                      | `CHOSEN (D24)`  | Ownership + PermissionAssignments instead of session attachment, and no 8-store or 2,000-memory caps; see [below](#memory-and-dreams)                                                                                                                                                                                              |
| Dream, a research preview (`dreams.md:11`)                                                                                                                                   | Dream (`POST /v1/dreams`)                                                                                                                                            | `CHOSEN (D56)`  | The closest to 1:1 on this page; see [below](#memory-and-dreams)                                                                                                                                                                                                                                                                   |
| Outcome (`user.define_outcome`) with a deliberately opaque grader — "you see that it's working, not what it's thinking" (`define-outcomes.md:589`)                           | [Outcome](/concepts/outcomes), same event name                                                                                                                       | `CHOSEN (D55)`  | Typed `report` outputs and a persisted grader rationale instead of an opaque grader; see [below](#outcomes)                                                                                                                                                                                                                        |
| Files API + `resources` mounts into the sandbox (`files.md:91`), edited on a live session                                                                                    | `POST /v1/files` + File and Project [mounts](/reference/sessions#mounts), attached and detached at turn boundaries                                                   | `CHOSEN (D43)`  | Same gesture; mounts settle observably via `session.mounts_resolved`. Where CMA edits a running session's resources, Checkfu applies the change at the next turn boundary (law 5) — for an idle Session the observable behavior is identical, and a Run never has the ground move under it                                         |
| GitHub repository resource carrying a caller-supplied `resources[].authorization_token` (`github.md:383`)                                                                    | Git-backed [Project](/concepts/projects) with governed push and pull-request writeback                                                                               | `CHOSEN (D29)`  | The PR flow is platform endpoints through a governed Connection, not a token handed to the clone; the mount plan itself is credential-free                                                                                                                                                                                         |
| Multiagent threads — the coordinator's roster snapshotted at create/update, one level deep (`multiagent-orchestration.md:234`, `:236`)                                       | A coordinator AgentVersion with an immutable resolved roster, CMA-shaped SessionThreads, and one shared SessionSandbox                                               | `MATCH`         | Checkfu freezes at publish rather than draft update, rechecks live `use_agent`/ActionPolicy, and requires a capability-declared compatible harness. The observable thread and shared-filesystem behavior matches CMA. See [Multiagent threads](/concepts/multiagent-threads)                                                       |
| Skills, including four pre-built Anthropic skills in every workspace and exact mounted-repository discovery (`skills.md:14`, `:374`)                                         | Versioned catalog Skills (`/v1/skills`), import/export and a curated first-party library, plus exact `.claude/skills/<name>/SKILL.md` discovery in frozen Git mounts | `CHOSEN (D53)`  | Catalog Skills pin at Session admission. Repository Skills remain unpinned mounted content and carry an explicit trust warning; exact-path discovery matches CMA, while the curated library remains reference-on-demand rather than ambient seeding                                                                                |
| Webhooks returning "the event `type` and `id`, not the full object" (`webhooks.md:12`)                                                                                       | [Webhook endpoints](/guides/receive-webhooks), same thin-envelope premise                                                                                            | `CHOSEN (D60)`  | One event catalog shared with the log and stream; see [below](#webhooks)                                                                                                                                                                                                                                                           |
| Self-hosted environment worker, a process that "receives tool execution requests from Anthropic" (`self-hosted-sandboxes.md:41`)                                             | [Runner](/getting-started/local-agents) with pairing (`POST /v1/runner-pairings`)                                                                                    | `CHOSEN (D9)`   | The Runner hosts the whole harness execution, not just tool calls; see [below](#self-hosted-execution)                                                                                                                                                                                                                             |
| Stream-only `event_deltas`, never persisted, with "no replay on reconnect" (`events-and-streaming.md:1855`)                                                                  | Stream-only deltas, never persisted                                                                                                                                  | `MATCH`         | Kept verbatim as a pattern — the durable log stays clean                                                                                                                                                                                                                                                                           |
| Four overlapping per-feature dated beta headers (`reference.md:13`)                                                                                                          | One dated `Checkfu-Version` header for the whole API                                                                                                                 | `CHOSEN (D2)`   | D2 names CMA's scheme as the explicit counterexample it is avoiding                                                                                                                                                                                                                                                                |

### One false friend

Checkfu has a resource named `ConnectionVault`, but it is not the CMA resource
with the similar name.

* A Checkfu **SandboxProfile** is the reusable base image, packages, network
  policy, resource limits, and retention configuration that maps to CMA's
  Environment. Each published **SandboxProfileRevision** is immutable. A multiagent Session's **SessionSandbox** is the one physical
  sandbox instance materialized from that configuration, not another reusable
  resource. Checkfu reserves **Environment** for deployment isolation.
* A **ConnectionVault** is a named access bundle over Connections. Editing it
  compiles ordinary `use_connection` PermissionAssignments; the bundle stores no provider
  credential and grants nothing by itself. Use a principal-owned **Connection**
  when migrating a CMA Vault credential.

<Note>
  This vocabulary is in motion. Checkfu has decided a final domain language
  (D262) that separates the deployment-stage sense of "environment" from the
  sandbox-configuration resource described above, and a mechanical rename is
  scheduled but has not landed. The names on this page describe the current
  wire surface; expect the sandbox-configuration resource's name to change in a
  future dated API version, announced in the changelog.
</Note>

## Creating a session

A CMA session takes an `agent` and an `environment_id`. A Checkfu Session takes an `agent_deployment_id` and a **`principal`** — the identity the Session acts as, which has no CMA equivalent and is required. The deployment is the Environment-scoped head whose revisions pin the Agent release plus the exact harness, model-routing, and sandbox coordinates the Environment resolves; "same agent, different sandbox or model route per Session" is multiple deployments of one Agent under distinct `key`s, not a per-Session override.

The SandboxProfile network shape ports directly. CMA's `allow_package_managers` and `allow_mcp_servers` exist on a Checkfu SandboxProfile under the same names, so a config copies across — with one difference to know about: a Checkfu flag is an abbreviation, not a mode. It compiles into the revision's explicit `allowed_hosts` at publication, so the SandboxProfile you read back shows every host the flag granted and enforcement never sees a flag at all. `allow_mcp_servers` is the case where matching the name does not mean matching the effect: it expands to no workload host, because a Checkfu sandbox never dials an MCP server itself — every MCP call already crosses the CapabilityGateway. Port it and you keep the reach; port it *alone*, with no other hosts on an `allowlist`, and publication refuses the SandboxProfile rather than accepting an allowlist that reaches nothing. See [Trust and isolation](/concepts/security).

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST https://api.checkfu.com/v1/sessions \
  --header "Authorization: Bearer $CHECKFU_API_KEY" \
  --header "Checkfu-Version: 2026-08-27" \
  --header "Idempotency-Key: sess-$TASK_ID" \
  --header "Content-Type: application/json" \
  --data '{
    "agent_deployment_id": "'"$AGENT_DEPLOYMENT_ID"'",
    "principal": "'"$PRINCIPAL_ID"'",
    "agent_deployment_revision_number": 3
  }'
```

Three wire habits change:

| CMA                                                                                               | Checkfu                                                                                                                                                               |
| ------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `anthropic-beta: managed-agents-2026-04-01`, plus per-feature betas such as `dreaming-2026-04-21` | One dated `Checkfu-Version` header for the whole API                                                                                                                  |
| No documented idempotency key for session creation or event submission                            | `Idempotency-Key` on mutating POSTs; an exact retry returns the first result                                                                                          |
| `agent` as an ID string (latest version) or a pinned-version object                               | `agent_deployment_id`, resolved to the deployment's current revision once at admission, plus an optional `agent_deployment_revision_number` to pin one exact revision |

Like CMA, a create call can carry the session's first work inline, and the cardinality now matches: both take up to 50 `initial_events`, processed in list order, and both accept `user.message` and `user.define_outcome`. Two Checkfu rules are worth knowing before you port a list across. A `user.define_outcome` is accepted only as the **first** entry, and at most one — an Outcome starts a turn from a settled boundary, which inside a create call is position 0 alone. And more than one entry requires an `Idempotency-Key` (the header CMA has no documented equivalent for, per the table above), because past the first entry the Run is already admitted and an exact retry is what recovers a list that failed part-way through.

CMA's third form — `agent_with_overrides`, which changes model, system prompt, tools, or skills for a single session — maps partially onto Checkfu's **session-variable slots**, and how far it maps depends on which field you are overriding.

`tools` and `skills` port with one difference in kind. Where CMA *replaces* the list with whatever you send, Checkfu **narrows** it: the release declares `session_variable.tools.allowed` (and `.skills.allowed`), those entries must be things the definition itself already grants — publication refuses a slot offering anything else — and a create call sends `tools` or `skills` naming a subset of the allowed set. So the gesture is the same and the direction is not: a Checkfu selection can take capability away from the reviewed release, never add capability it did not grant.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST https://api.checkfu.com/v1/sessions \
  --header "Authorization: Bearer $CHECKFU_API_KEY" \
  --header "Checkfu-Version: 2026-08-27" \
  --header "Content-Type: application/json" \
  --data '{
    "agent_deployment_id": "'"$AGENT_DEPLOYMENT_ID"'",
    "principal": "'"$PRINCIPAL_ID"'",
    "tools": ["search"]
  }'
```

`model` and CMA's environment selection do not port: the deployment revision pins the exact model-routing and sandbox coordinates, so per-Session variance there means multiple deployments of one Agent under distinct `key`s — a `stable` and a `canary`, say — not a per-Session override on the create wire. That is the price of "a Session always runs something a human reviewed."

**`system`** ports as `instructions`, and it is the one member of `agent_with_overrides` that needs no declaration on the definition. Instructions are content rather than capability: an override reaches no tool, Skill, IntegrationConnection, PermissionAssignment, sandbox, or egress rule the release did not already grant, and your own first message steers behaviour inside that same envelope already — so there is nothing for an allowed-list to bound. What you get in exchange for that freedom is a record: the prompt is frozen into the Session's admitted release and pinned by digest in `run.created.execution_pins.instructions_sha256`, so two sessions of one release that ran different prompts stay distinguishable forever. It is bounded at 64 KiB, and a Session created through an installed surface refuses it — that definition comes from the installation, not from the caller.

**`mcp_servers`** has no per-session form at all: a ToolSource's tools reach a Session by being declared on the definition, and the Connection backing one is pinned there too, so the `tools` slot can only subtract from that declared set — it cannot add or replace a server for one session.

CMA's separate idle-session update — a session operation rather than an override — ports as `POST /v1/sessions/{id}/selection`. The Session must be idle with no live execution, exactly as CMA requires, and the body is a replacement in the same way: send `tools` to narrow, omit it to restore the release's full declared set, and one call does both. Every selection is admission-checked again, PermissionAssignments included, so a permission revoked between turns denies the re-selection rather than riding on the first admission. What it cannot do is move a pinned execution coordinate: if the harness release, sandbox image, model routing profile, deployment revision, or an environment policy has changed under the Session, the re-selection is refused and you make a new Session. The boundary lands on the event log as a `session.updated` entry carrying the release hash either side; *what* each turn ran is already in that Run's `execution_pins`.

A coordinator agent whose roster includes itself narrows like any other release: the selection travels to its native child threads, so a child spawned against the `self` entry runs the narrowed set rather than the release's full one.

CMA's per-session budget ports directly. Where CMA takes `budget: { max_list_cost }` at session create, a Checkfu create call takes `budget: { limit, metric? }`, which compiles into a real session-scoped [Budget](/concepts/tenancy-and-governance) — visible on the budgets list, not a shadow cap. The semantics match CMA's: the session pauses at the cap (idle with `stop_reason: "budget_reached"`, never terminated), the one in-flight model call completes and is billed, and raising the Budget's limit strictly above consumed usage re-opens admission — restarting work that was queued behind the pause without a fresh message, exactly like CMA's auto-resume. An Automation's agent target accepts the same optional `budget` field, applied per created session, covering CMA's per-run deployment budgets. Two differences to know: Checkfu bills its own reviewed price catalog in integer micro-USD (`model_spend`) or provider tokens (`model_tokens`) rather than CMA's list-cost components (web searches and sandbox wall-clock are not yet counted), and CMA's one-way `budget: null` removal maps to pausing the Budget — reversible, and unlike CMA you can also attach a budget to an existing session later.

Inference effort works the same way on both platforms and is worth mapping directly. CMA sets `effort` inside the agent's `model` object (the `model_config` form) and explicitly ignores it inside a per-session override. Checkfu declares effort on the model candidate as a closed, harness-agnostic scale — `minimal`, `low`, `medium`, `high`, `max` — which the model gateway translates per provider. CMA's rungs are `low`, `medium`, `high`, `xhigh`, `max`, so `low` through `high` and `max` line up by name; CMA has no `minimal` and Checkfu has no `xhigh`. Checkfu never passes a provider's raw effort parameter through.

## Events: the same taxonomy, one catalog

### Events you send

CMA's `events.send` takes a batch (`events: [...]`); Checkfu's takes **one** discriminated event with a `payload`, and attribution is required on every input: `authored_by` (the Principal speaking) and `caused_by` (what triggered the turn).

| CMA                       | Checkfu                   | Note                                                                                                                                                                                           |
| ------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `user.message`            | `user.message`            | Accepted even while the Session is still provisioning; it orders for the next Run                                                                                                              |
| `user.interrupt`          | `user.interrupt`          | Add `session_thread_id` to target one thread; omit it to interrupt all non-archived threads                                                                                                    |
| `user.custom_tool_result` | `user.custom_tool_result` | Echo `session_thread_id` for a child wait; accepted only for the exact current `tool_use_id` generation                                                                                        |
| `user.define_outcome`     | `user.define_outcome`     | Same rubric + `max_iterations` shape; may also ride in `initial_events`                                                                                                                        |
| `user.tool_confirmation`  | `user.tool_confirmation`  | Same Session event route; child responses echo `session_thread_id`, while Checkfu retains ActionApproval policy, responder authority, and idempotency fences                                   |
| `system.message`          | `system.message`          | Adds privileged context for subsequent Runs without replacing the frozen Agent instructions. It requires a control-plane role and a model route that supports mid-conversation system context. |

### Events you read

| CMA                                                                                                                                                              | Checkfu                                                                                    | Note                                                                                                                                                                                                                                                                              |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `agent.message`, `agent.thinking`, `agent.tool_use`                                                                                                              | Same names                                                                                 | Plus `agent.tool_result` and `agent.question`. One content difference: CMA's `agent.thinking` is a progress signal that carries no thinking content, while Checkfu's carries the reasoning text subject to retention                                                              |
| `agent.custom_tool_use`                                                                                                                                          | `agent.tool_use`, then `run.requires_action` with `action.kind: "custom_tool"`             | The park is an explicit event carrying the `tool_use_id` to answer                                                                                                                                                                                                                |
| `agent.mcp_tool_use`                                                                                                                                             | `agent.tool_use`                                                                           | One shape for every tool kind; the governed ToolInvocation is recorded as `exec.tool_invocation`                                                                                                                                                                                  |
| `session.status_running`, `session.status_idle`                                                                                                                  | Same names                                                                                 | `session.status_idle` carries a typed `stop_reason`; treat only `session.status_completed` / `_failed` / `_canceled` as unconditional terminals, because idle with `stop_reason.type: "requires_action"` means "awaiting your input"                                              |
| `session.error`                                                                                                                                                  | `session.status_failed` (Session) and `run.failed` (attempt)                               | Failures are statuses with reasons; every execution attempt is a first-class Run                                                                                                                                                                                                  |
| `session.status_terminated`                                                                                                                                      | `session.status_completed` **or** `session.status_failed` **or** `session.status_canceled` | CMA emits one `terminated` for both a clean finish and an unrecoverable error, and the event carries no reason — its payload is `{ id, processed_at, type }` — so the cause has to be correlated from a preceding `session.error`. Checkfu keeps three distinct terminal statuses |
| —                                                                                                                                                                | `run.*` (`run.created`, `run.started`, `run.requires_action`, `run.completed`, …)          | CMA has no Run resource for interactive turns; Checkfu records one for every execution attempt                                                                                                                                                                                    |
| `span.model_request_start` / `span.model_request_end`                                                                                                            | `model.routed`, `model.usage_recorded`                                                     | No span telemetry; see [No equivalent, on purpose](#no-equivalent-on-purpose)                                                                                                                                                                                                     |
| `span.outcome_evaluation_start` / `_ongoing` / `_end`                                                                                                            | `outcome.evaluation_started`, `outcome.evaluation_completed`                               | First-class domain events with ledger-backed cost, not telemetry                                                                                                                                                                                                                  |
| (session pauses for `always_ask`)                                                                                                                                | `action_approval.pending`, `action_approval.resolved`                                      | The pending ask and its resolution are both durable log events                                                                                                                                                                                                                    |
| `session.thread_created`, `session.thread_status_running`, `session.thread_status_idle`, `session.thread_status_rescheduled`, `session.thread_status_terminated` | Same names and lifecycle payloads                                                          | Checkfu keeps its resumable event envelope around the CMA-shaped payload; see [below](#multiagent-frozen-rosters-and-one-coordinator-session)                                                                                                                                     |

### Resuming a stream

Both platforms stream over SSE with stream-only deltas for token-by-token rendering. The reconnect story differs: CMA has no resume cursor, so a client that drops must re-list persisted history, seed seen IDs, then tail and deduplicate — and missed stream-only deltas are gone. That replay is timestamp-keyed, and the key recently moved: CMA now orders its event lists by each event's `processed_at` rather than `created_at`, and its `created_at[gt]` / `[lt]` filters compare against `processed_at` despite the parameter names. Checkfu events carry a monotonic per-session `seq`, and the stream honors **`Last-Event-ID`**: send the last sequence you fully processed and the stream resumes from there. Ordering and cursoring are the same integer, so there is no timestamp semantics to track. See [Resume a stream](/guides/resume-a-stream).

## Tool permissions and approvals

CMA's `permission_policy` is two-state (`always_allow` / `always_ask`) and configured per toolset on the agent. Checkfu's [ActionPolicy](/concepts/tenancy-and-governance) is a separate resource with three dispositions — `allow` / `require_approval` / `deny`, resolved as `deny > require_approval > allow`. A rule names a subject kind from a closed set — `principal`, `group`, `agent_definition`, `agent_installation`, `surface_scope`, `session`, or `*` — and a resource kind, each with a glob over ids, plus a glob over the permission. Defaults: a read-only, non-destructive tool over a read-only transport is allowed; writes and destructive-hinted tools require approval. An MCP-bound tool is never auto-allowed, because its safety hints are authored by the upstream server and refresh on every sync — it reaches `allow` only through an explicit ActionPolicy.

When an ActionPolicy says `require_approval`, the ask is not an inline event exchange but a durable [ActionApproval](/concepts/action-approvals): a queryable resource with frozen resolution context (connection, principal, arguments), a TTL-bound one-time response token, and the responder's identity on the record. You list pending ActionApprovals, `GET` one, and `POST /v1/action-approvals/{id}/responses` — from any surface, not just the client holding the stream. The "stop asking me" gesture maps to a **StandingApproval**: minted only as a side effect of an approve response, scoped to one exact tuple, TTL-capped, revocable, and audited — never a silent config edit.

One scoping difference to notice: CMA's permission policies **do not apply to custom tools** — your application is responsible for deciding whether to execute one. In Checkfu, custom (host-side) tools go through the same ActionPolicy evaluation, so an ActionPolicy can escalate or deny them before your app ever sees the call.

### The defaults you arrive with

Where CMA sets a *preset*, Checkfu resolves the same two answers and now names them in CMA's own spelling (D216), so nothing about the switch is a surprise:

| CMA preset                                           | Checkfu                                                                                                                            | What it means with no ActionPolicy configured                                                                                    |
| ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| MCP toolset `always_ask`                             | `default_permission` on the ToolSource, `always_ask`                                                                               | A connected tool goes to a human on each use. Uniform across ToolSource kinds, because every kind crosses the CapabilityGateway  |
| Agent toolset `always_allow`                         | `always_allow`, the posture an AgentDefinition's built-in toolset resolves under — a property of the boundary, not a field you set | Built-ins never reach the CapabilityGateway, so scoping them is `agent_toolset`'s job rather than ActionPolicy's                 |
| `default_config.enabled: false` with per-tool allows | `default_enabled: false` on the ToolSource                                                                                         | Every discovered tool arrives disabled until enabled by name — including one that appears upstream after you reviewed the source |

The subsetting posture is the one to set deliberately when you port a CMA config: it is fixed at ToolSource creation, and a later sync preserves each tool's explicit review rather than re-deciding it. See [Tool sources](/concepts/capabilities#the-two-defaults-a-source-carries).

## Tool results and session output files

Two CMA behaviors around large content have direct Checkfu counterparts worth
knowing before you port a data-heavy workload:

* **Oversized tool results.** Both platforms bound one tool result at 100,000
  characters. CMA spills the remainder to a sandbox file and hands the model a
  preview plus the path. Checkfu's Docker Runner now matches that recovery
  shape: its self-describing `result_truncated` envelope keeps a bounded preview
  and names a `live_only` path in the active sandbox. Read it during the Run;
  it is never captured into Session outputs and dies with the sandbox. An
  execution plane without the Runner spill sink, including the Worker-hosted
  Runner, returns the same bounded envelope without a path and states the loss
  honestly rather than promising unavailable recovery (D202/D272).
* **Reading what a session wrote.** CMA mounts agent output into the sandbox
  and leaves retrieval to you. Checkfu captures files the agent writes under
  `/mnt/session/outputs` at settlement and serves them from the API:
  `GET /v1/sessions/{id}/files` lists captured outputs and
  `GET /v1/sessions/{id}/files/content?path=…` returns the bytes (D178).
  Capture is bounded and streamed into Session-owned staged custody before the
  terminal event: at most 64 MiB per file and 256 MiB across at most 64 files.
  A file over the per-file ceiling is recorded `too_large`; a file inside it
  that cannot enter the settlement's file-count, aggregate-byte, or Session
  storage-quota budget is recorded `over_capacity`. Either way it is recorded
  with its typed reason rather than silently dropped, so a bounded capture never
  reads as an empty one. A new Runner talking to an older control plane falls
  back to the legacy inline transport and its 1 MiB/file and 8 MiB/settlement
  limits, again reporting `over_capacity` visibly. On the read side one response
  serves at most 8 MiB; `offset` and `length` name exact byte windows that can be
  concatenated to reconstruct a larger captured file. ZDR refuses both the
  streaming settlement protocol and retained output files.

## Multiagent: frozen rosters and one coordinator Session

CMA's multiagent orchestration runs coordinator threads inside one session: a config-snapshotted roster (at most 20 agents, 25 concurrent threads), one shared sandbox and vault set, and delegation depth 1. Checkfu mirrors the coordinator authoring boundary: `multiagent.agents` accepts a bare Agent id, an exact versioned Agent, or `self`, publishing freezes the complete resolved roster, and callers start one pinned coordinator Session. The public runtime is one Session with a primary thread and persistent child threads:

* The published roster defines addressability. Live `use_agent` [PermissionAssignments](/concepts/tenancy-and-governance) and ActionPolicy can revoke a target but cannot add one.
* Each member is pinned to an exact AgentVersion when the coordinator is published. Bare ids advance only when the coordinator is republished; exact entries remain pinned; `self` is supported directly.
* Each child has a stable `sthr_*` identity, complete filtered history, resumable stream, cumulative usage/stats, and `running | idle | rescheduling | terminated` lifecycle. No backing execution identifier is public.
* Spawning queues one initial message and returns immediately; follow-ups address the same persistent thread, child reports/questions wake the primary, and the primary never joins or parks on child completion.
* Sent/received event bodies match CMA's direction-relative shape. Checkfu's two private platform-control tools are an implementation boundary, not CMA API names or customer thread-mutation operations.
* The primary is listed first, children follow spawn order, and the Session admits at most 25 non-archived threads including primary. Idle children persist for follow-up until archived.
* Publication rejects nested coordinators and members that cannot share the coordinator runtime. Every active thread runs as an isolated process in one fenced SessionSandbox and sees shared file changes immediately.
* One aggregate-idle winner performs lifecycle writeback and checkpointing. The environment expires no later than day 30 without extending on activity; a later turn gets fresh scratch while thread conversation remains.
* The roster is capped at 20 unique Agents and 500 aggregate Skills.

The runtime and porting guide is in [Multiagent threads](/concepts/multiagent-threads).

## Memory and Dreams

CMA memory stores attach to sessions (at most 8 per session) and cap out at 2,000 memories per store. A Checkfu [MemoryStore](/concepts/memory) is a versioned file tree with an **ownership boundary** (`environment`, `principal`, `agent_definition`, `session`, or `surface_scope`) where access is always PermissionAssignments — there are no fixed store-count or memory-count caps; cost is bounded by Budgets rather than quotas. Shared-owner stores advance through reviewed proposals instead of live sandbox writes, and every read and write lands in the log (`memory.accessed`, `memory.proposal_created`, …).

Dreams map almost 1:1. Both are asynchronous jobs (`POST /v1/dreams`) that take one existing memory store plus a set of past sessions and produce a **new** output store, copy-on-write, with the inputs never mutated, plus `cancel` and `archive` operations. Checkfu adds a deterministic mode (omit the model routing profile: dedupe only, zero usage) and a `review` flag that lands the result as an open proposal for a human decision. Both Versioned Memory and Dreams are currently `rollout_fenced` (Dreams additionally `preview`) — check [capability status](/getting-started/status) before planning a memory-dependent migration.

## Outcomes

The shape is kept: `user.define_outcome` with a description, rubric, and bounded `max_iterations`; a grader in a separate context; a terminal result taxonomy. Two departures:

* **The wire.** CMA emits grader progress as `span.outcome_evaluation_*` telemetry while also driving control flow and billing from it. Checkfu's `outcome.evaluation_started` / `outcome.evaluation_completed` are first-class domain events with ledger-backed cost.
* **The verdict.** CMA's grader is deliberately opaque ("you see that it's working, not what it's thinking"). Checkfu persists the grader's `explanation` and the evaluation-input digest, subject to retention, and an Outcome can declare 1–32 **typed `report` outputs** — validated primitives with acceptance criteria — when downstream systems need comparable values instead of a verdict. See [Outcomes](/concepts/outcomes).

## Webhooks

The premise is identical — thin envelope with the event `type` and `id`, never payload content; fetch the object on receipt; at-least-once delivery with retries; an HMAC signature keyed by a `whsec_`-prefixed secret. The signing shapes differ: CMA sends the three Standard Webhooks headers (`webhook-id`, `webhook-timestamp`, `webhook-signature`), Checkfu sends one `Checkfu-Signature` header with a timestamped `t=…,v1=…` scheme.

Retry behaviour differs in a way worth planning for. CMA makes up to three delivery attempts with jittered backoff between 5 and 120 seconds, then drops the event with no further signal. Checkfu makes seven attempts spanning about 17 hours, with backoff widening from 30 seconds to 12 hours. Both are at-least-once and neither is a durable log: deduplicate on the event `id` and reconcile from the API when you need every transition.

The difference is the catalog. CMA's webhook event names diverge from its persisted event names (`session.status_run_started` versus `session.status_running`, `session.status_idled` versus `session.status_idle`). Checkfu normally uses the persisted event name across streams and webhooks; the CMA-shaped child-thread lifecycle intentionally follows CMA's webhook spelling: `session.thread_created`, `session.thread_idled`, and `session.thread_terminated`. Its signed thin envelope adds `data.session_thread_id` beside the Session and Environment identities. The rest of the subscribable platform lifecycle includes `session.status_*`, `run.*`, `action_approval.pending` / `action_approval.resolved`, and `outcome.evaluation_*`. See [Receive webhooks](/guides/receive-webhooks).

CMA additionally publishes resource-lifecycle webhooks — created / archived / deleted across agents, deployments, environments, vaults, vault credentials, and memory stores, plus `updated` on the first three, `paused` / `unpaused` on deployments, and `refresh_failed` on vault credentials. Checkfu's equivalent family covers memory stores (`memory_store.created` / `.archived` / `.deleted` — including the platform-created output store a completed dream leaves behind, which no customer call created), agents (`agent.created` / `.updated` / `.archived`; published versions are immutable, so publishing delivers `agent.updated`), Vault Credentials (the exact `vault_credential.refresh_failed` event, including the owning `vault_id` beside the Credential identity), connections (`connection.created` / `.revoked` / `.refresh_failed` for Checkfu's additional governed Connection aggregate), automations (`automation.created` / `.paused` / `.resumed` / `.deleted` — the `deployment.paused` / `.unpaused` analogs; Automations have no archive state, so deletion is the terminal transition), deployment Environments (`workspace.created` / `.updated`; disabling is a versioned update, and Checkfu exposes no Environment delete/archive operation), and reusable sandbox configuration as SandboxProfiles (`sandbox_profile.created` / `.updated`; creation publishes revision 1 and later revision publication is the update). D262 requires the extra noun: CMA's reusable sandbox Environment maps to Checkfu SandboxProfile, while Checkfu Environment is the isolated deployment boundary. A later Checkfu Environment creation delivers through the pre-existing request Environment; bootstrap creates the first Environment and has no possible Environment-scoped subscriber, so it emits no fact rather than race an empty catalog. Every delivery is the signed thin envelope with `data.resource_id` beside a `null` session, so one get-by-id completes the read. An Automation subscription trigger observes subscribable platform events, not Checkfu-internal resource mutations (D198).

## Self-hosted execution

CMA's `self_hosted` environment is a work queue: Anthropic's runtime drives the agent loop, and your environment worker polls for tool-execution work items and posts results back. Checkfu keeps the outbound-only worker-polling pattern and widens what it hosts: a [Runner](/getting-started/local-agents) executes the **entire harness** — the agent loop, the sandbox, the tools — on your infrastructure, while the control plane keeps the durable log and governance. Pairing is explicit: an admin mints a short-lived code naming the acting Principal (`POST /v1/runner-pairings`), the Runner redeems it, and revoking the Runner's credential fences new claims. Provider credentials still never leave custody: the sandbox receives only the same short-lived gateway capability a hosted sandbox would.

The practical consequence of that wider scope shows up in what a self-hosted session can carry. CMA rejects any session that names a `resources` entry on a self-hosted environment, so files, repositories, and memory stores are hosted-only there. A Checkfu Session keeps the same mounts on a Runner as on a managed sandbox, because the Runner owns the filesystem the harness runs against.

## The walked migration

Everything above is a map. This is the territory: the CMA quickstart agent and
environment, taken verbatim from Anthropic's own tutorial, translated, published
as an [AgentBlueprint](/concepts/agent-blueprints), applied, and driven through a
real Session. The transcript below is recorded output from an actual run, not an
illustration.

The input is the CMA quickstart's `agent` (`quickstart.md:151-156`) and
`environment` (`quickstart.md:347-350`), plus the two things a real integration
carries beyond a tutorial — a vault attached at session create and a metadata bag:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "agent": {
    "name": "Coding Assistant",
    "model": "claude-opus-5",
    "system": "You are a helpful coding assistant. Write clean, well-documented code.",
    "tools": [{ "type": "agent_toolset_20260401" }],
    "metadata": { "external_user_id": "usr_abc123" }
  },
  "environment": {
    "name": "quickstart-env",
    "config": { "type": "cloud", "networking": { "type": "unrestricted" } }
  },
  "vault_ids": ["vlt_quickstart"]
}
```

Recorded 5 August 2026 against a local worker:

```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
STEP 1 — translate the CMA configuration
  agent            "Coding Assistant" → blueprint `coding-assistant`
  model            claude-opus-5 → ModelRoutingProfile `claude-opus-5`
  networking       unrestricted → `unrestricted`
  refusals         2

  REFUSED resource_metadata_unsupported  (agent.metadata)
  what   `external_user_id`
  why    CMA carries an arbitrary key-value `metadata` bag for the customer's own
         tracking (guides/agent-setup.md:30, api/beta/agents.md:258-260); Checkfu
         has no correlation-metadata home on these resources today.
    track  V-API-012

  REFUSED vault_reference_unresolved  (vault_ids)
  what   `vlt_quickstart` — referenced as Connection `vlt_quickstart`, which must
         exist in the target Environment
  why    CMA attaches environment-scoped vault credentials per session by `vault_ids`
         (guides/vaults.md:21); Checkfu has no credential vault — a ConnectionVault
         is a named access bundle that stores no secrets and grants nothing, while a
         CMA Vault credential migrates to a principal-owned Connection plus a
         `use_connection` PermissionAssignment (D107) — so a placeholder is
         emitted and apply reports it unresolved rather than binding a credential
         nobody authorized.

STEP 2 — publish the AgentBlueprint
  version_id       abp_38592de97c1f4077a6ed9433009daa5b
  blueprint        coding-assistant v1
  release_hash     sha256:b93a3a63361051174ef2b056316eaf5a60da00d6777383477b8aedb81450fddb

STEP 3 — apply it
  installation     bins_f1fa97fda9ce4f1bbf0731759b53ef1f
  lifecycle        applied
    harness_profile   reused    hprof_89c5f1064e0c4833a4f7344cf4bd7741
    model_routing_profile reused  mrp_31e09ec26b829d7fd9378d921030ac79
    environment       reused    sprof_cff503700aafe311540695f940aa37e2
    agent_definition  created   agent_e275bbfc88ec8bbd02dbf25658900424
    permission_assignment created perm_5cce66f698db1a9d7761d89c97da6691
    permission_assignment created perm_2bc5f92049530adf7c9df1641b481ecb
    permission_assignment created perm_6090b5d8c34293421fab7e4960c2d134

STEP 4 — assign the Principal `invoke` on the installed definition
  permission_assignment perm_36fbbefd8f3efa794fe3c7ae325e9949
  permission            invoke
  definition            agent_e275bbfc88ec8bbd02dbf25658900424 (draft v2)
  published             v1

STEP 5 — drive a real Session on what apply installed
  session          sess_a32479cde2665ddb2bc388b1e5a1a5b3
  status           pending

STEP 6 — the event log
      1  session.status_pending
      2  session.admitted
      3  user.message
      4  run.created
      5  session.mounts_resolved
      6  session.status_provisioning
      7  run.started
      8  session.status_running
      9  agent.message
     10  run.completed
     11  session.status_idle

  settled          session.status_idle
  events           11 (seq 1..11)
  agent reply      "[checkfu mock harness] received: Summarize what you can do."

WALK OK — 2 named refusals, installation bins_f1fa97fda9ce4f1bbf0731759b53ef1f, 11 events
```

Four things in that transcript are worth reading twice.

**The refusals are the product, not the errors.** Nothing was dropped quietly.
Each one names the CMA field, quotes the reason with the page it came from, and —
where the behavior is itemized — points at the ledger item tracking it. The
refusal list is a per-customer gap report you can hand to whoever has to approve
the switch.

<Note>
  The transcript is a recording, so it is left exactly as it ran — including a
  refusal that has since been closed. `V-API-012` has moved to `built` since 5
  August 2026: resources now carry a customer-owned metadata bag, so a re-run
  today would bind `external_user_id` rather than refuse it. The
  `vault_reference_unresolved` refusal beside it is not a gap and will not close —
  it is D107 working as designed, and the paragraph below explains why.
</Note>

**A vault becomes a placeholder, never a guess.** `vlt_quickstart` translated to a
Connection *reference*, so apply reports it unresolved instead of binding some
credential nobody authorized. You then create the Connection deliberately, which
is the whole point of the [Connection](/concepts/capabilities) model.

**Step 3 minted three PermissionAssignments you did not have to write.** A CMA agent reaches its
environment because the session named it. A Checkfu AgentDefinition holds an
explicit PermissionAssignment per runtime resource — and because the Blueprint declared them,
apply created them, and you can list and revoke each one.

**Step 4 has no CMA counterpart, and that is the difference.** The Principal a
Session acts as must hold `invoke` on the definition. In CMA, a workspace API key
can reference anything in the workspace; here, reach is a row.

One caveat stated plainly: the local stack runs a mock harness that echoes its
input, so the **reply text** is fixture content. The Session, the publish, the
apply, the PermissionAssignments, the event log, the sequence numbers, and the settlement are
real — what the transcript proves is that the migration path works end to end,
not that a model wrote that sentence.

## What you lose today

These are the current customer-visible gaps relative to CMA. Each row comes from
Checkfu's outcome ledger, cites CMA's mirrored documentation, and disappears
when its status is promoted past `partial`, `planned`, or `undecided`.

*No customer-visible CMA parity gaps are currently recorded.*

Availability is separate from parity: a built behavior can still be
`rollout_fenced` in a particular deployment. Check [capability
status](/getting-started/status) before planning a migration.

## No equivalent, on purpose

Places CMA has a surface and Checkfu deliberately does not. Each entry says what to use instead.

| CMA surface                                                                                  | Checkfu position                                                                                                                                                                                                                                                                                                                                             |
| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`span.*` telemetry** (`span.model_request_*`, `span.outcome_evaluation_*`)                 | Spans are unenforceable telemetry, so nothing that drives control flow or billing lives there. Model activity is `model.routed` / `model.usage_recorded` events plus the usage ledger; grader activity is `outcome.*` domain events.                                                                                                                         |
| **`agent_with_overrides`'s `mcp_servers`** (adding or replacing MCP servers for one session) | A ToolSource is registered once per workspace and the definition names the tools it exposes, so the servers a Session can reach are a reviewed property of the release. The `tools` slot narrows that set per Session, [above](#creating-a-session); adding an unreviewed server for one session is the thing the published-version model exists to prevent. |
| **Content-bearing webhooks**                                                                 | Neither platform ships these; the thin envelope is a kept premise, not a gap. Fetch the event by `id` on delivery.                                                                                                                                                                                                                                           |
| **`max_turns`**                                                                              | Neither platform enforces a turn cap server-side (CMA's own migration guide says to count turns client-side). Bound work with [Budgets](/concepts/automations#budgets) and an Outcome's `max_iterations` instead.                                                                                                                                            |

## Next steps

<CardGroup cols={2}>
  <Card title="Quickstart" icon="rocket" href="/getting-started/quickstart">
    Create an Agent, publish a version, and drive a Session end to end.
  </Card>

  <Card title="Events reference" icon="list-timeline" href="/reference/events">
    The full event catalog this page's tables are drawn from.
  </Card>

  <Card title="Handle an approval" icon="user-check" href="/guides/handle-an-approval">
    Review frozen context, answer through a Session or ActionApproval resource, and serve a Custom-tool result.
  </Card>

  <Card title="Migrate from your own loop" icon="arrows-rotate" href="/guides/migrate-from-your-own-loop">
    The companion guide for hand-rolled agent loops.
  </Card>
</CardGroup>
