> ## 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.

# Install an agent with a Blueprint

> Author one reviewed package, validate and plan it locally, then record its installation: the repeatable path to a whole governed agent.

<Warning>
  This guide is migration-fenced. Blueprint inline-Agent application still
  targets the retired AgentRelease/AgentDeployment lifecycle and is not an
  executable CMA Agent journey. Use [Agents](/concepts/agents) for the current
  direct Agent contract.
</Warning>

An [AgentBlueprint](/concepts/agent-blueprints) is one reviewed, versioned package
that composes a governed agent from Blueprint-managed and preprovisioned
resources (its definition, surface, connections, PermissionAssignments, budgets, and
automations) instead of a dozen hand-coordinated requests. This guide walks
the authoring-to-installation path.

<Warning>
  The Checkfu CLI has no self-service public installation channel during private
  alpha. CLI steps in this guide assume access through an explicitly authorized
  alpha arrangement. See [CLI access in private alpha](/getting-started/cli-access)
  for the source-checkout and HTTP alternatives.
</Warning>

An AgentBlueprint is not an integration recipe. A Blueprint declaratively
reconciles governed Checkfu resources; [`checkfu add`](/guides/use-checkfu-with-your-coding-agent)
prints write-nothing instructions for a coding agent to build customer-owned
adapter or UI code.

`CHECKFU_API_KEY` and `CHECKFU_WORKSPACE_ID` come from
[Get access](/reference/access#the-three-variables-ready); the setup script
below mints `CHECKFU_SETUP_ID` itself. The TypeScript tabs use the
[TypeScript SDK](/reference/typescript-sdk) with this client:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { Checkfu } from "@checkfu/sdk"

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

<Steps>
  <Step title="Author the package" titleSize="h2">
    A Blueprint is a directory with a closed manifest and an optional inline agent:

    ```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
    my-agent/
      checkfu.json          # the schema_version 3 manifest
      agent/                # instructions.md, plus optional manifest.json, tools/, skills/
      scenarios/            # acceptance scenarios
    ```

    Two rules shape everything in the manifest. First, resources are named by
    **logical name**, never by ID, which is what lets one version promote across
    stages unedited. Second, every resource carries a **disposition** saying who
    owns it: `create` (the Blueprint manages and reconciles it), `reference` (it
    must already exist and is never mutated), or `adopt` (bind an existing one and
    manage it from now on). Credentials are declared as **requirements**. A
    package never carries a value.

    A referenced Agent and every managed Automation target use the same reusable
    deployment selector. `current` resolves once for each admitted Session;
    `pinned` names one exact AgentDeployment revision:

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "source": "reference",
      "name": "tag-assistant",
      "agent_deployment_revision_selection": { "type": "current" }
    }
    ```

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "agent": "tag-assistant",
      "agent_deployment_revision_selection": {
        "type": "pinned",
        "revision_number": 7
      }
    }
    ```

    The first object is the manifest's exact `agent` value; the second is an
    Automation's exact `target` value. Start from a complete bundled template
    because the top-level manifest schema is closed.

    Copy a bundled template rather than starting from an empty manifest. Both
    compile into ordinary resources. They ship as on-disk directories in a
    Checkfu source checkout, under `packages/blueprint/templates/`, and are not
    published to a package registry:

    ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
    cp -R packages/blueprint/templates/tag-assistant ./my-agent
    ```

    `tag-assistant` is the Slack-to-GitHub assistant the rest of this guide
    follows; its logical names are the ones step 2 provisions.
    `proactive-governed-review` is the complete signed-ingress, Outcome,
    DeliveryScreen, and passive FlowReview control loop. Each template's
    top-level `README.md` documents what it installs and what you must supply;
    it travels beside the package and is not part of it.

    The rest of this step is the declaration rules for the resource kinds people
    get wrong most often.

    **Schedule and webhook Automations** declare their full ordinary configuration, with
    nothing inferred: `created_by` names a person from the manifest's `people`
    list (reference-only, so a package can never mint a human), `acted_as` names
    the Blueprint service Principal, and the target Agent, optional SurfaceScope,
    `agent_deployment_revision_selection`, cron and timezone, prompt or Outcome,
    and desired pause state are each explicit. Apply resolves those logical names
    to Workspace IDs and records the Automation it created. The older
    name-plus-trigger-kind shape
    still parses, but only under `reference`: it cannot create or adopt authority.
    A webhook create/adopt/update recipe additionally names an Automation-bound secret
    requirement. Its value never enters the package: supply it through apply's
    bounded `webhook_secret_inputs` sidecar, where it is consumed by the ordinary
    Automation create or managed rotation patch and omitted from every returned artifact.

    **Installed surface exposure** uses exact scope declarations; the older
    `{ "kind", "selector" }` entries are reference-only. A named `installation`
    binds three things together: an AgentDeployment (either the referenced Agent's
    selected deployment revision or the exact revision this apply produced for an
    inline directory), the service Principal, and an active ExternalInstallation
    with its active scopes, once their default governance has converged. Upgrading
    an inline agent publishes a new AgentRelease and deployment revision, then
    repins the placement to that new immutable revision. Note that the
    ExternalInstallation itself is referenced, not created. Creating one needs
    the short-lived pairing proof, which is its own flow.

    **Budgets** either take `environment` scope with no target, or name a declared
    logical resource with `scope: "agent_definition" | "principal" | "surface" |
            "automation"` plus `target: "<logical-name>"`. Apply resolves that name to the
    exact aggregate ID in the target Workspace. A caller-supplied ID is never
    treated as authority. An Automation-scoped Budget is applied after the
    Automation it points at.

    **MemoryStores** follow the same shape: `owner: "environment"` with no target, or
    `owner: "principal" | "agent_definition" | "surface_scope"` paired with
    `target: "<logical-name>"`. Session-owned stores come from a live Session, not
    from a package. Workspace-, AgentDefinition-, and SurfaceScope-owned stores
    remain distinct ownership categories, but schema v3 does not project the
    portable logical names in top-level `memory.mounts` into executable agent
    defaults. Validation rejects a nonempty top-level mount list, and apply blocks
    a previously published package that contains one before any aggregate
    mutation. Concrete agent `memory_mounts` remain the separate D19 behavior
    field; do not mistake an installation receipt for runtime mount authority.
  </Step>

  <Step title="Provision the referenced resources" titleSize="h2">
    The template adopts a service Principal named `tag-bot` and references a
    Connection named `github-main`. Provision both through their ordinary APIs
    before apply. The Connection must be owned by `tag-bot`, because that is the
    Principal the installed agent acts as.

    First create the service Principal and keep its ID:

    <CodeGroup>
      ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
      export CHECKFU_SETUP_ID="$(openssl rand -hex 16)"

      export CHECKFU_TAG_BOT_ID="$(
        curl --fail --silent --show-error \
      	--request POST "https://api.checkfu.com/v1/organizations/workspaces/$CHECKFU_WORKSPACE_ID/principals" \
          --header "Authorization: Bearer $CHECKFU_API_KEY" \
          --header "Checkfu-Version: 2026-08-27" \
          --header "Idempotency-Key: tag-bot-$CHECKFU_SETUP_ID" \
          --header "Content-Type: application/json" \
          --data '{
      		"external_subject_id": "tag-bot",
      		"principal_type": "service",
            "display_name": "Tag Bot"
          }' | jq --raw-output '.id'
      )"
      ```

      ```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
      // Keep this stable across retries of the whole setup.
      const setupId = crypto.randomUUID()

      const tagBot = await checkfu.principals.create(
      	process.env.CHECKFU_WORKSPACE_ID,
      	{
      		external_subject_id: "tag-bot",
      		principal_type: "service",
          display_name: "Tag Bot",
        },
        { idempotencyKey: `tag-bot-${setupId}` },
      )
      ```
    </CodeGroup>

    Then authorize the GitHub Connection with the exact label, provider, scopes,
    owner, and egress used by the template:

    <CodeGroup>
      ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
      export CHECKFU_CONNECTION_AUTH="$(
        curl --fail --silent --show-error \
          --request POST https://api.checkfu.com/v1/connections/authorize \
          --header "Authorization: Bearer $CHECKFU_API_KEY" \
          --header "Checkfu-Version: 2026-08-27" \
          --header "Idempotency-Key: github-main-$CHECKFU_SETUP_ID" \
          --header "Content-Type: application/json" \
          --data '{
            "owner_principal_id": "'"$CHECKFU_TAG_BOT_ID"'",
            "provider": "github",
            "label": "github-main",
            "scopes": ["repo:read", "pr:write"],
            "egress_rules": [{
              "scheme": "https",
              "host": "api.github.com",
              "port": 443,
              "path_prefix": "/",
              "methods": ["GET", "POST", "PATCH"],
              "allowed_headers": ["accept", "content-type"]
            }]
          }'
      )"

      printf '%s\n' "$CHECKFU_CONNECTION_AUTH" | jq --raw-output '.connect_url'
      ```

      ```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
      const authorization = await checkfu.connections.authorize(
        {
          owner_principal_id: tagBot.id,
          provider: "github",
          label: "github-main",
          scopes: ["repo:read", "pr:write"],
          egress_rules: [
            {
              scheme: "https",
              host: "api.github.com",
              port: 443,
              path_prefix: "/",
              methods: ["GET", "POST", "PATCH"],
              allowed_headers: ["accept", "content-type"],
            },
          ],
        },
        { idempotencyKey: `github-main-${setupId}` },
      )

      console.log(authorization.connect_url)
      ```
    </CodeGroup>

    Open the printed HTTPS URL and complete the hosted authorization before it
    expires. No GitHub token enters the Blueprint or this API request. Before
    applying, read the returned `.connection.id` with `GET /v1/connections/{id}`
    and confirm `lifecycle: "active"`, `health: "healthy"`, `drift: "none"`, and
    `custody_revoke_pending: false`.

    The example also references an existing person Principal named `owner`, a model
    routing profile named `default-model`, an active Slack ExternalInstallation named
    `primary-workspace`, and the `engineering` and `owner` Slack scopes. Its
    Workspace must provide the `default-harness` profile and the declared Slack and
    GitHub capabilities.
  </Step>

  <Step title="Validate locally" titleSize="h2">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    checkfu blueprint validate ./my-agent
    ```

    This checks the package structure and its intrinsic semantics (excessive privilege, scope conflicts, duplicate names, missing governance) and prints the resolved preview. No keys required. It exits non-zero on any error, so it drops straight into a pre-commit hook or CI.
  </Step>

  <Step title="Preview the plan" titleSize="h2">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    checkfu blueprint plan ./my-agent
    ```

    The plan lists the ordered actions apply would take (`create`, `reuse`, `update`, `publish`, `replace`, or `blocked`) plus the release hash and the missing-credential steps:

    ```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
    plan tag-assistant · release sha256:…
      update   principal tag-bot
      reuse    connection github-main
      reuse    model_routing_profile default-model
      create   budget workspace-tokens
      credential github-app-token: supply github-app-token to connection github-main …
    ✓ ready to apply
    ```

    Planning never changes anything. The local plan has no live hash for the
    adopted Principal, so it conservatively previews `update`; an exact live
    `tag-bot` is reused and recorded as adopted. The local plan also assumes
    referenced resources exist, so its credential line is a custody check deferred
    to the server. A live apply resolves Workspace authority. If `github-main` is
    absent, it records a non-mutating planned installation with a visible action:

    ```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
      blocked  connection github-main — create or import connection github-main
    ```

    An unhealthy, drifted, or reauthorization-required Connection similarly leaves
    `github-app-token` owed. Restore it through the Connection's hosted,
    write-only authorization flow; never add the credential value to the package.
  </Step>

  <Step title="Apply the Blueprint" titleSize="h2">
    Applying reconciles the Blueprint into the ordinary resources server-side and records the outcome as a **BlueprintInstallation**: the resolved resources with created-versus-reused ownership, any still-owed inputs, and the lifecycle. Apply from the CLI:

    ```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
    checkfu blueprint apply ./my-agent
    ```

    ```text theme={"theme":{"light":"github-light","dark":"github-dark"}}
    applied tag-assistant · applied · release sha256:…
      reused  principal tag-bot → prin_…
      reused  connection github-main → conn_…
      created budget workspace-tokens → bgt_…
      reused  model_routing_profile default-model → mrp_…
    ```

    The CLI first publishes the directory to `POST /v1/organizations/{organization_id}/agent-blueprints`. The server parses and canonicalizes the package, derives its metadata and content hash, and returns an immutable release ID scoped to your Organization. The CLI then applies that release:

    ```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
    POST /v1/blueprint-installations/apply
    ```

    The request carries `agent_blueprint_release_id`; the authenticated API key's Workspace is the only target. A package that creates or rotates the signing value of a webhook Automation also carries a bounded write-only sidecar such as `"webhook_secret_inputs": [{ "requirement": "incident-webhook-secret", "value": "whsec_…" }]`. The requirement must exactly match the Automation declaration; values are compared only against the exact stored Automation version, consumed during reconciliation, and never returned or persisted in the installation. An unchanged reapply may omit the sidecar. When the webhook declaration changes, a missing exact sidecar fails before the installation or Automation is mutated. Raw package bytes are not apply authority, and neither are the `available_references` and `credentialed` compatibility hints older clients may still send. The server re-reads the immutable published package, derives references from it, proves that each concrete resource exists in the target Workspace, and records its ID.

    Credential requirements complete only through an exact server-side custody check; a requirement form without a settled exact slot selector fails closed as `planned`. A known unresolved reference likewise leaves a non-mutating `planned` installation, so naming a resource or credential cannot make it exist. The response is the recorded **BlueprintInstallation**:

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "bins_…",
      "blueprint_name": "tag-assistant",
      "agent_blueprint_release_id": "abrel_…",
      "applied_version": 1,
      "release_hash": "sha256:…",
      "lifecycle": "applied",
      "resolved_resources": [
        { "resource_kind": "principal", "logical_name": "tag-bot", "resource_id": "prin_…", "ownership": "reused", "management": "adopted", "manage_mode": "reconcile", "config_hash": "…" },
        { "resource_kind": "connection", "logical_name": "github-main", "resource_id": "conn_…", "ownership": "reused", "manage_mode": "none", "config_hash": null },
        { "resource_kind": "model_routing_profile", "logical_name": "default-model", "resource_id": "mrp_…", "ownership": "reused", "manage_mode": "none", "config_hash": null }
      ],
      "missing_inputs": []
    }
    ```

    `GET /v1/blueprint-installations` lists what a Workspace has installed, and `GET /v1/blueprint-installations/{id}` reads one for reproducibility and drift.
  </Step>
</Steps>

## Operate over time

* **Promotion is currently fail-closed.** `POST /v1/blueprint-installations/promote` retains the dated caller-reported evaluation request for wire compatibility, but returns `policy.denied` because Checkfu has not published a platform receipt issuer. A local `checkfu eval` report is unverified; saving or resubmitting it never upgrades its trust. The denial happens before package publication, reconciliation, or installation mutation, so the prior release stays active.
* **Roll back** by re-applying an immutable prior release. Apply is idempotent and upgrade-aware, so re-applying an earlier package restores it; nothing published is mutated.
* **Uninstall** revokes Blueprint-owned PermissionAssignments, removes Blueprint-owned ActionPolicies, disables or archives Blueprint-managed AgentInstallations, detaches Blueprint-managed SurfaceScopes, and pauses Blueprint-managed Automations before marking the installation uninstalled. This includes adopted exposure when its effective management mode is `reconcile` or `replace`; references and `adopt` with `manage: none` remain read-only. No data-bearing resource is deleted: MemoryStores, Connections, principals, transcripts, and artifacts are retained. `POST /v1/blueprint-installations/{id}/uninstall` requires an `Idempotency-Key`; an interrupted attempt resumes from live aggregate state under the same operation fence. Erasure remains a separate, explicit request.

## Resource capability matrix

The schema accepts dispositions more broadly than every aggregate adapter can
currently execute. This is the supported apply behavior for the resource kinds
used by `tag-assistant`:

| Resource kind                 | `create`                                                                                                                 | `reference`                                                                   | `adopt`                                                                                        |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| Inline AgentDefinition        | Publish and reconcile                                                                                                    | A referenced published Agent is supported instead of inline source            | Not an authored disposition                                                                    |
| HarnessProfile                | No; use the ordinary reviewed adoption and conformance flow                                                              | Reuse the exact resolved profile and allow PermissionAssignments to target it | Not an authored disposition                                                                    |
| ModelRoutingProfile           | No create adapter                                                                                                        | Reuse an existing profile                                                     | Read-only exact bind with `manage: none`; no drift-reconcile adapter                           |
| SandboxProfile                | Create when the full template is declared                                                                                | Reuse an existing SandboxProfile                                              | Exact bind only; no drift-reconcile adapter                                                    |
| ExternalInstallation          | No; use the separate pairing-proof flow                                                                                  | Reuse an active installation                                                  | Read-only exact bind with `manage: none`; pairing and mutation remain separate                 |
| SurfaceScope                  | Create from an exact declaration                                                                                         | Reuse an exact or historical selector                                         | Bind and reconcile an exact declaration                                                        |
| Service Principal             | Create and reconcile                                                                                                     | Reuse without mutation                                                        | Bind and reconcile an exact existing Principal                                                 |
| Person Principal              | Not expressible                                                                                                          | Reuse without mutation                                                        | Not expressible                                                                                |
| Connection                    | **No create adapter**                                                                                                    | Reuse an active, healthy, exact provider/scope match                          | Read-only exact bind with `manage: none`; no Connection reconciliation adapter                 |
| PermissionAssignment defaults | Create and replace as Blueprint-managed defaults                                                                         | Not an authored disposition                                                   | Not an authored disposition                                                                    |
| ActionPolicy defaults         | Create and replace as Blueprint-managed defaults                                                                         | Not an authored disposition                                                   | Not an authored disposition                                                                    |
| Budget                        | Create and reconcile                                                                                                     | Reuse without mutation                                                        | Bind and reconcile                                                                             |
| MemoryStore                   | Create and reconcile                                                                                                     | Reuse without mutation                                                        | Bind and reconcile                                                                             |
| Schedule Automation           | Create and reconcile from a complete schedule                                                                            | Reuse an existing Automation                                                  | Bind and reconcile from a complete schedule                                                    |
| Webhook Automation            | Create and reconcile, including managed signing-value rotation, with an exact Automation-bound write-only secret handoff | Reuse an existing Automation                                                  | Bind and reconcile, including managed signing-value rotation, with the same write-only handoff |

Historical SurfaceScope selectors and historical thin Automation recipes are
reference-only. Connection creation is deliberately absent rather than
unimplemented: `checkfu blueprint plan` will not imply it, so provision the
Connection through `POST /v1/connections/authorize` as shown above.

## How scope trees converge

An installed-agent Blueprint either references existing SurfaceScopes or creates
an exact declared scope tree beneath an existing active ExternalInstallation.
Order matters and apply enforces it: created roots converge before their declared
children, and the AgentInstallation is created only once every scope is active.
Creating the ExternalInstallation itself still needs its separate short-lived
pairing proof.

Later applies reconcile what is mutable, meaning scope governance and a
nonterminal disabled state. Four things never move: provider identity, kind,
installation, and parent. Archive is operator authority, it is terminal, and it
blocks reapply.
