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

# Connect a provider account

> Embed Checkfu's provider-neutral Connect Center, complete setup, choose agent tools, and revoke without handling provider credentials.

A [Connection](/concepts/capabilities#connections) is one authorized third-party
account your agents can call without seeing its credential. Connect Center is
the preferred public workflow for creating and governing those accounts. It
uses the same operations and `@checkfu/ui` component in Checkfu's dashboard and
in your own application.

Your backend needs a Checkfu API key and Workspace. The browser does not.

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

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

// This can be a service Principal for a shared account or an end-user
// Principal for a delegated personal account.
const center = createConnectCenterController(checkfu, ownerPrincipalId)
```

Register the embedding application's exact HTTPS origin through the public,
versioned Workspace registry. Paths may vary beneath an admitted origin, but
credentials, HTTP origins, subdomain wildcards, query-only origins, and
request-derived host headers are never redirect authority.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const currentOrigins = await checkfu.integrationGateway.returnOrigins.retrieve()

await checkfu.integrationGateway.returnOrigins.replace(
  {
    expected_version: currentOrigins.version,
    origins: ["https://app.example.com"],
  },
  { idempotencyKey: crypto.randomUUID() },
)
```

Removing an origin blocks new ceremonies immediately; an already-reserved
ceremony keeps only its frozen, non-extendable return URL until expiry. Local
end-to-end ceremonies therefore need an HTTPS development origin or tunnel—
plain loopback HTTP is intentionally not an OAuth/credential return origin.

## List what this Principal can connect

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const offerings = await center.list()
```

Each offering is already joined to only that Principal's Connections. It
includes:

* Checkfu-owned package name, revision, digest, and review tier;
* minimum scopes and exact reviewed egress destinations;
* normalized tool count and qualification expiry;
* connection flow: managed or protocol OAuth, hosted credential entry, no
  credential step, or Integration Bridge pairing;
* opaque hosted, dedicated, or customer-Bridge placement choices and their
  availability; and
* the Principal's account lifecycle, health, and optimistic version.

Render every returned row. `curated`, `structural`, and `unreviewed` are facts
for the person deciding what to connect, not reasons to silently hide a row.
An unavailable row or placement carries a closed reason such as
`qualification_expired` or `not_paired`.

The projection never contains a provider credential, supplier tenant id,
account handle, adapter key, Bridge handle, or provider authorization URL.

## Render the shared Connect Center

Use the stateful `ConnectCenterAdapter`, not a second application-specific
connection state machine. Give it the signed-in Principal id and narrow
browser-to-BFF transport functions; your BFF maps those functions to
`createConnectCenterController` while retaining the Checkfu Workspace key.
The [credential broker example](https://github.com/andyrewlee/checkfu/tree/main/examples/credential-broker)
is the complete forkable transport and server implementation.

```tsx theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { ConnectCenterAdapter } from "@checkfu/ui/sdk"
import { checkfuConnectCenterBff } from "./checkfu-connect-center-bff"

<ConnectCenterAdapter
  principalId={signedInPrincipalId}
  transport={checkfuConnectCenterBff}
  onChanged={() => refreshAccountSummary()}
  onError={(cause) => reportConnectionError(cause)}
/>
```

The `ConnectCenterTransport` type is the BFF contract. It includes listing,
begin/reconnect/revoke, read/observe/replay of one session, source detection,
credential-free account detail, access plan/apply, and revocation impact. Each
function accepts already-scoped ids and returns the public SDK projection; it
must never return your Checkfu key, a provider credential, or a supplier URL.

The adapter owns search, account labels, minimum-scope display, stable retry
keys, one secret-free active-session locator per Principal, reload recovery,
bounded observation, tool/agent selection, and inventory refresh. Its default
storage is the current tab's `sessionStorage`, so a pending ceremony can resume
after reload without sharing recovery state across tabs. It owns no API key,
provider credential, supplier coordinate, or durable Checkfu authority.

## Begin setup

Choose one exact offering and one available opaque placement. Keep one
idempotency key for the logical attempt until the server responds; reusing it
after an ambiguous timeout prevents a duplicate Connection.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const attemptKey = crypto.randomUUID() // retain for retries

const session = await center.begin(
  {
    offering_id: selectedOffering.id,
    placement_id: selectedPlacement.id,
    label: "work",
    return_url: "https://app.example.com/settings/connections",
  },
  { idempotencyKey: attemptKey },
)
```

Omitting scopes and egress rules adopts the qualified package defaults. A
caller may request a narrower subset; widening is rejected before adapter I/O.
The placement id is only a public choice—the server resolves the frozen runtime
route and private coordinates.

Before custody begins, the server resolves one exact published
`ProviderPackage` and enforces its credential flow, minimum and allowed scopes,
and egress ceiling. The browser never asserts those binding facts, and
caller-authored settings cannot bypass a missing or ambiguous package.

The result owns one durable `ConnectionSession` and may carry a short-lived
Checkfu-origin `nextAction`:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
if (session.nextAction !== null) {
  session.open((url) => window.open(url, "_blank", "noopener,noreferrer"))
}
```

That hosted step handles OAuth consent, pasted API keys, service-account
material, or Bridge pairing. Secret bytes terminate inside the selected trusted
custody boundary — Checkfu's sealed native store, or the selected
IntegrationRuntime adapter's own store — and at that boundary's final outbound
edge. They do not cross the UI kit, your browser payloads, the model, the
sandbox, the secret-free IntegrationRuntime port, or an audit event.

For managed OAuth suppliers that require their own confirmation page, Checkfu
wraps the supplier URL in a one-time Checkfu-origin handoff and completes the
supplier's custom verifier on the control plane. The raw supplier URL is never
part of the public session JSON.

## Observe or replay the exact session

`ConnectionSession` is the durable cursor. Observe it instead of searching a
environment-wide account list by label:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const settled = await session.observe({
  pollIntervalMs: 2_000,
  timeoutMs: 120_000,
  onChange: (current) => renderProgress(current),
})
```

The observer reconciles through the session's frozen runtime route. It settles
as `connected`, `revoked`, `failed`, `expired`, or `canceled`; it never returns
a provider response or credential. There is no Connection-lifecycle webhook
today, so this helper uses bounded public polling.

If a live human action was lost, replay that exact session under another
caller-owned retry key. Replay cannot create a Connection or broaden scope,
egress, package, placement, or provider authority.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const replayed = await session.replay({ idempotencyKey: retainedReplayKey })
```

## Detect a remote MCP or API source

Detection is credential-free and informative. It never publishes, enables, or
connects a source.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const detection = await center.detect("https://tools.example.com/mcp")
// matched | qualification_required | unavailable
```

A recognized MCP/OpenAPI source that has no qualified offering reports its
kind and bounded tool count with `qualification_required`. Network, document,
size, and protocol failures are closed reasons rather than guessed defaults.

## Pair a private environment

Use Integration Bridge when the tool is a private HTTP API, a local CLI, or a
stdio MCP server that Checkfu's hosted plane cannot reach. Operate one Bridge
per customer environment or trust zone—not per agent, Principal, Connection,
or end user. Ordinary remote integrations use Checkfu's shared regional plane;
dedicated hosted cells are exceptions for explicit compliance, residency, or
private-network requirements.

An administrator creates the Workspace-owned placement and receives its
short-lived, one-use pairing code only in that create response:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const pairing = await checkfu.integrationGateway.bridges.create(
  { name: "production", environment: "corp-west" },
  { idempotencyKey: crypto.randomUUID() },
)
```

Install the released `checkfu-integration-bridge.mjs` artifact on a Node.js 22
machine that can reach the local targets. The local mode-`0600` config contains
only Checkfu's qualification-generated manifests plus fixed local target
coordinates and credentials. Pair and verify it before starting the service:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
node checkfu-integration-bridge.mjs pair --config /etc/checkfu/bridge.json --code '<pairing.code>'
node checkfu-integration-bridge.mjs doctor --config /etc/checkfu/bridge.json
node checkfu-integration-bridge.mjs run --config /etc/checkfu/bridge.json
```

The daemon opens only outbound HTTPS to Checkfu. Agent JSON can become only a
reviewed HTTP body/query value, MCP argument object, or CLI stdin; it cannot
choose an origin, executable, process argument, environment variable, working
directory, shell, or MCP tool name. It durably records a pending receipt before
each local effect, refuses ambiguous crash recovery instead of repeating the
effect, and rechecks exact live Workspace, Principal, Connection, revision,
placement, and request authority immediately before execution.

The daemon rotates its Bridge-scoped machine credential automatically every
day. The last current generation can renew for at most 30 days after expiry so
a temporarily disconnected environment can recover; beyond that ceiling,
revoke the stale Bridge and pair a replacement. After two minutes without an
authenticated claim, Connect Center projects the Bridge as `offline` and omits
it from new placement choices without deleting already queued signed work. A
later valid claim projects it as `active` again.

List Bridge presence without credentials or routing handles through
`checkfu.integrationGateway.bridges.list()`. Revocation invalidates the exact
machine credential locally before it returns:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
await checkfu.integrationGateway.bridges.revoke(
  bridge.id,
  { expected_version: bridge.version },
  { idempotencyKey: crypto.randomUUID() },
)
```

## Choose tools and agent access

A connected account is still inert. Load its credential-free detail, review
the selected tools and agents, then plan before applying:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const detail = await center.detail(settled.connection_id)

const plan = await center.planAccess(
  detail.connection.id,
  selectedAgentDefinitionIds,
  selectedToolNames,
)

const applied = await center.applyAccess(
  detail.connection.id,
  selectedAgentDefinitionIds,
  selectedToolNames,
)
```

The compiler creates explicit Connection and tool authority and reports
`installation_required`, `publish_required`, or `ready`. “All current agents”
is an explicit reconciled set, never ambient authority for future agents.

During a Run, the harness calls the exact `connection_id:tool_name` through
CapabilityGateway. Checkfu rechecks PermissionAssignments, ActionPolicy and approval, Connection health
and revision, package/binding digest, exact egress, budget, and invocation
idempotency before credentials are injected server-side.

## Reconnect and revoke

Reconnect reserves a distinct immutable successor revision. The predecessor
remains the admitted revision until the successor completes.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const reconnect = await center.reconnect(
  detail.connection,
  "https://app.example.com/settings/connections",
  { idempotencyKey: retainedReconnectKey },
)
```

Revocation denies locally before supplier cleanup. It is version-fenced and
also requires one retained logical-attempt key:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const revoked = await center.revoke(detail.connection, {
  idempotencyKey: retainedRevokeKey,
})
```

The returned session may be terminal `revoked` with `cleanup_pending: true`.
New ToolInvocations are denied throughout asynchronous supplier cleanup.

## Drive the same lifecycle from the CLI

The private-alpha CLI composes the same generated operations; it has no
supplier-specific connector command or separate state. Start with the
Principal-scoped offering wall:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
checkfu connection offerings --principal prin_... --json
```

Begin setup with one selected offering and opaque placement. The CLI reserves
a new mode-`0600` file before it sends the request, writes the complete
human-completion receipt there, and prints only the secret-free session
summary:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
checkfu connection connect \
  --principal prin_... \
  --offering ioff_... \
  --placement hosted \
  --label "Engineering Slack" \
  --return-url https://app.example.com/settings/connections \
  --output ./slack-connect-receipt.json
```

Open `next_action.url` from that protected file outside model context. Retain
the returned `cns_...` identity for passive reads, explicit runtime
reconciliation, or a lost-handoff replay:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
checkfu connection session cns_... --principal prin_... --json
checkfu connection refresh cns_... --principal prin_... --json
checkfu connection replay cns_... --principal prin_... \
  --output ./slack-replay-receipt.json
```

Here `refresh` means reconcile that durable `ConnectionSession`; it is not a
capability-snapshot refresh. Read an Integration Gateway account's
credential-free health and drift from the Connection projection. Do not route
it through the legacy custody health mutation:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
checkfu connection health conn_... --principal prin_... --json
```

Review the credential-free tool catalog, then compile the complete desired
AgentDefinition/tool set before applying it. Repeated `--agent` and `--tool`
flags are explicit selections; use `--no-agents` or `--no-tools` when an empty
set is intentional. Omitting `--tool` selects every currently discovered tool.

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
checkfu connection tools conn_... --principal prin_... --json
checkfu connection access plan conn_... \
  --principal prin_... \
  --agent agent_... \
  --tool messages.send \
  --json
checkfu connection access apply conn_... \
  --principal prin_... \
  --agent agent_... \
  --tool messages.send \
  --confirm --json
```

The plan changes nothing. Apply reconciles the complete AgentDefinition set,
may change draft tool pins and PermissionAssignments, never publishes an Agent automatically,
and therefore requires confirmation.

Reconnect uses the version from that reviewed Connection read and protects its
new handoff in the same way. Revocation has a separate impact preview and
requires both the returned CAS version and explicit confirmation:

```sh theme={"theme":{"light":"github-light","dark":"github-dark"}}
checkfu connection reconnect conn_... \
  --principal prin_... \
  --expected-version 3 \
  --return-url https://app.example.com/settings/connections \
  --output ./slack-reconnect-receipt.json

checkfu connection impact conn_... --principal prin_... --json
checkfu connection revoke conn_... \
  --principal prin_... \
  --expected-version 3 \
  --confirm --json
```

## Ownership modes

<Columns cols={2}>
  <Card title="Workspace / service">
    Use a service Principal for one shared account. Its blast radius is every
    explicitly authorized agent acting through that Principal.
  </Card>

  <Card title="Personal / delegated">
    Map each application user to an end-user Principal. The Principal-scoped
    offering read and session routes keep divergent accounts at one provider
    separate.
  </Card>
</Columns>

The flow is otherwise identical. A foreign Principal cannot list, observe,
replay, reconnect, or revoke another Principal's account or session.

## Lower-level compatibility operations

The generated SDK still exposes direct Connection authorize, provision,
health, and revoke operations for advanced and migration clients. They are not
the Connect Center contract and require callers to compose more lifecycle and
catalog state themselves. New embedded products should use the provider-neutral
controller above. The former duplicate connector path has been removed; these
lower-level operations remain only for native custody-backed Connections and
advanced lifecycle clients.
