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

> ## Agent Instructions
> Base URL: https://api.checkfu.com. Current wire version: `2026-08-31` (`Checkfu-Version: 2026-08-31`).
> Mutating HTTP requests require `Authorization: Bearer $CHECKFU_API_KEY`, `Checkfu-Version: 2026-08-31`, `Content-Type: application/json`, and `Idempotency-Key` on retriable writes. Ordinary API keys are Workspace-bound; do not send a Workspace selector header.
> Core workflow: create an Agent (`POST /v1/agents`) and Environment (`POST /v1/environments`), grant `use_environment` (`POST /v1/permission-assignments`), create a Session (`POST /v1/sessions`), send events (`POST /v1/sessions/{id}/events`), stream progress (`GET /v1/sessions/{id}/events/stream`), and read the input result (`GET /v1/sessions/{id}/inputs/{input_id}/result`).
> Domain model: an Agent is mutable configuration; a Session freezes its AgentVersion and Environment at creation and owns its private workspace and ordered event history. Vault stores secret credentials; Connection links an external account; ActionApproval records human approval before an action runs.
> Capability status combines `audience`, `stability`, `availability`, evaluated prerequisites, and release evidence; check https://docs.checkfu.com/getting-started/status (`GET /v1/support/capabilities`) for live `available` status.

# Contribute from a private Session

> Let a person's private Session add a document to a team's shared Memory, but only the exact bytes that person approves.

A person's [private Session](https://docs.checkfu.com/guides/build-a-personal-assistant.md) can read a team's shared [Memory](https://docs.checkfu.com/concepts/memory.md) store, but it can't write to it: a store the Session doesn't own mounts only `read_only`. A personal contribution is how something learned in that Session reaches the team anyway. The Session asks to write exact documents, the person approves those bytes, and the approval becomes a Memory proposal in the team's store under that person's name. Nothing else from the Session goes with it.

This guide sets up the store and the Session, then shows the person's approval and the team's review.

Before starting, have:

* A Workspace-bound `CHECKFU_API_KEY` that can administer grants (root or admin).
* The person's Principal (`prin_…`) in `CHECKFU_PRINCIPAL_ID`, with `invoke`, `steer` and `act_as` on the Agent (see [Build a personal assistant](https://docs.checkfu.com/guides/build-a-personal-assistant.md#1-set-up-access-once)).
* An Agent on any harness. Pi, ACP (`acp-v1`) harness images and connected runtimes that serve the Run control MCP offer `checkfu_memory_contribute` as is; the Container harnesses (`claude-code`, `codex`, `deepseek`) see it as `mcp__checkfu__checkfu_memory_contribute`. A Workspace with zero data retention offers it nowhere.
* An Environment (`env_…`) with no network access, as every personal Session requires.

> [!WARNING]
>
> Plain HTTP against the Checkfu API needs no private package.
> The Checkfu CLI and TypeScript SDK are optional private-alpha artifacts; the HTTP requests work without them.
> Part of this flow is behind a rollout fence; a deployment that has not enabled it refuses the request rather than running it.
> Confirm [capability status](https://docs.checkfu.com/getting-started/status.md) before making availability part of your application's contract.

## 1. Share a team store

Create a Workspace store that reviews changes, and grant it. The person needs `read` to mount it and `write` because the contribution lands under their own grant. The Agent needs `read`. A curator needs `read` and `approve` to promote proposals.

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

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

const team = await checkfu.memoryStores.create(
  { name: "Support playbook", owner: { kind: "workspace" }, canonical_writes: "review" },
  { idempotencyKey: "playbook-store" },
)

const grants = [
  [{ type: "principal", id: personId }, "read"],
  [{ type: "principal", id: personId }, "write"],
  [{ type: "agent", id: agentId }, "read"],
  [{ type: "principal", id: curatorId }, "read"],
  [{ type: "principal", id: curatorId }, "approve"],
] as const
for (const [subject, permission] of grants)
  await checkfu.permissionAssignments.create(
    { subject, resource: { type: "memory_store", id: team.id }, permission },
    { idempotencyKey: `playbook-${team.id}-${subject.id}-${permission}` },
  )
```

A store the Session owns is never a destination: the Session writes its own store through its mount.

## 2. Mount it in the person's private Session

Create a `personal` Session acting as the person, with the team store mounted read-only:

```bash cURL 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-31" \
  --header "Idempotency-Key: contribution-session-001" \
  --header "Content-Type: application/json" \
  --data '{
    "agent": { "type": "agent", "id": "agent_0123456789abcdef0123456789abcdef", "version": 3 },
    "environment_id": "env_0123456789abcdef0123456789abcdef",
    "acted_as": "prin_0123456789abcdef0123456789abcdef",
    "audience": { "type": "personal" },
    "resources": [
      { "type": "memory_store", "memory_store_id": "memstore_0123456789abcdef0123456789abcdef", "access": "read_only" }
    ]
  }'
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const session = await checkfu.sessions.create(
  {
    agent: { type: "agent", id: agentId, version: currentVersion },
    environment_id: environmentId,
    acted_as: personId,
    audience: { type: "personal" },
    resources: [{ type: "memory_store", memory_store_id: team.id, access: "read_only" }],
  },
  { idempotencyKey: `contribution-session-${requestId}` },
)
```

Asking for `read_write` on the team store is refused, because the person's Session is narrower than the team.

## 3. The Session asks to contribute

A `personal` or `group` Session is offered one Checkfu tool for this, `checkfu_memory_contribute`. Its input names a store the Session mounts read-only and up to 16 document operations on distinct paths, with at most one document's size of content in all:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "memory_store_id": "memstore_0123456789abcdef0123456789abcdef",
  "operations": [
    { "op": "put", "path": "/refunds/eu.md", "content": "EU refunds over 100 EUR need a second reviewer.\n" }
  ]
}
```

Tell the Agent when to use it, for example in its system prompt: "When you learn something the whole support team should know, contribute it to the playbook store." A `put` may carry `expected_sha256` (the document it replaces, or `null` to create it); a `delete` must.

The tool writes nothing. It records one [ActionApproval](https://docs.checkfu.com/guides/handle-an-approval.md) of kind `memory_contribution` that binds the store and the exact operations, and answers `approval_requested` with its `action_approval_id`. The Run doesn't wait: the Agent ends its turn and tells the person what's waiting. The same bytes asked again in the same Run return the same approval. Changing the store or any byte, or asking in another Run, is a new approval. Once the person declines, the tool answers `declined`, and the Agent shouldn't ask again unless the person asks it to.

A `workspace` Session, or one that names no audience, isn't offered the tool, since it may write shared stores through ordinary grants.

## 4. The person approves the exact bytes

List the Session's approvals and show the person what would land. While it's pending, `context_summary.operations` holds the exact operations:

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --get https://api.checkfu.com/v1/action-approvals \
  --header "Authorization: Bearer $CHECKFU_API_KEY" \
  --header "Checkfu-Version: 2026-08-31" \
  --data-urlencode "session_id=sess_0123456789abcdef0123456789abcdef"
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
for await (const approval of checkfu.actionApprovals.list({ session_id: session.id })) {
  if (approval.kind !== "memory_contribution" || approval.status !== "pending") continue
  showToPerson(approval.memory_store_id, approval.context_summary.operations)
}
```

Only the person the Session acts as can approve. Record their decision with `acted_as` set to them:

```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  "https://api.checkfu.com/v1/action-approvals/$APPROVAL_ID/responses" \
  --header "Authorization: Bearer $CHECKFU_API_KEY" \
  --header "Checkfu-Version: 2026-08-31" \
  --header "Idempotency-Key: contribution-$APPROVAL_ID" \
  --header "Content-Type: application/json" \
  --data '{
    "expected_version": 1,
    "decision": "approve",
    "acted_as": "prin_0123456789abcdef0123456789abcdef"
  }'
```

```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const decided = await checkfu.actionApprovals.decide(
  approvalId,
  { expected_version: approval.version, decision: "approve", acted_as: personId },
  { idempotencyKey: `contribution-${approvalId}` },
)
// The proposal the contribution became in the team store.
const proposalId = decided.memory_proposal_id
```

A Workspace administrator's key acting as itself, another person, or a service is refused with `403`, and the approval stays pending. For a `group` Session created by a key acting as itself, the [boundary approval](https://docs.checkfu.com/concepts/sessions-and-runs.md#boundary-approval) rules decide who may answer: an active person in the audience who holds `approve`. A StandingApproval or `always_allow` never covers a contribution. The person can also answer in the Console.

On approval, Checkfu lands the operations once, as a MemoryProposal authored by the approving person under their current `write` grant. The approval then drops its copy of the bytes (`context_summary.operations` becomes `null`) and names the proposal in `memory_proposal_id`. Approving again, or replaying the request, answers the same proposal. If the person has lost `write`, or a document changed under an `expected_sha256`, nothing lands: the response is refused and the approval is spent with no proposal. A transient failure keeps the approval reserved, and approving again finishes it.

## 5. The team reviews it

The contribution is now an ordinary proposal, so the store's own review applies. A curator promotes or rejects it, in the Console or with their own credential:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
await checkfu.memoryStores.proposals.respond(
  team.id,
  proposalId,
  { decision: "approve" },
  { idempotencyKey: `promote-${proposalId}` },
)
```

Once promoted, the document is the team's, under the team store's permissions and retention. Nothing ties it to the Session: deleting the person's Session later leaves the team's copy, and the Session's transcript, other Memory, Files and checkpoint never left it.

## What this leaves out

* A direct write. Every contribution becomes a proposal, even on a store with `canonical_writes: "direct"`.
* A Console card that shows a contribution's documents. The Console marks it as a boundary approval; read the operations from the API.
* Unanswered approvals. One that expires keeps its bytes in storage until the Session is deleted.


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