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

# Service entry messages

> Provision ExternalInstallations and AgentPlacements, then send an installed Agent messages with POST /v1/agent-placements/{id}/messages.

An AgentPlacement is a service entry: one installed [Agent](https://docs.checkfu.com/concepts/agents.md) that another system calls as a Principal of your Workspace. The caller sends text messages to `POST /v1/agent-placements/{id}/messages` and reads back the Session, status and answers it is allowed to see.

> [!NOTE]
>
> Plain HTTP against the Checkfu API needs no private package.
> The documented path is usable as written for an admitted Workspace.
> Confirm [capability status](https://docs.checkfu.com/getting-started/status.md) before making availability part of your application's contract.

## Installations and placements (`exti_...` and `aplace_...`)

You do not need an `ExternalInstallation` or `AgentPlacement` for standard API [Sessions](https://docs.checkfu.com/concepts/sessions-and-runs.md) (`POST /v1/sessions`). Provision them when another system calls an installed Agent, when you enroll a [ConnectedRuntime](https://docs.checkfu.com/guides/connect-a-local-agent.md), or when you route watched-resource subscriptions:

1. **ExternalInstallation (`exti_...`):** Issue a short-lived signed pairing proof (`POST /v1/external-installations/pairing-proofs`) and pass `pairing_proof` to `POST /v1/external-installations` to link an external tenant (`provider` and `external_tenant_id`) to your Workspace.
2. **AgentPlacement (`aplace_...`):** Bind an `Agent`, `environment_id`, `external_installation_id`, `identity_posture` (`autonomous` or `delegated`), `acted_as`, and `harness` via `POST /v1/agent-placements`.

## Authenticate the caller

The caller authenticates as its own `service` or `person` Principal with an `api`-audience [Principal access credential](https://docs.checkfu.com/reference/mcp.md), never with a Workspace API key. Grant that Principal `invoke` on the `agent_placement` resource with a [PermissionAssignment](https://docs.checkfu.com/concepts/tenancy-and-governance.md).

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl https://api.checkfu.com/v1/principals/prin_fedcba9876543210fedcba9876543210/access-credentials \
  -H "Authorization: Bearer $CHECKFU_API_KEY" \
  -H "Checkfu-Version: 2026-08-31" \
  -H "Idempotency-Key: partner-service-credential" \
  -H "Content-Type: application/json" \
  --data '{"audience":"api"}'
```

Every send re-checks the active placement, the credential, and the caller's live PermissionAssignments. Revoking `invoke` stops the next send.

## Send a message

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl \
  "https://api.checkfu.com/v1/agent-placements/aplace_0123456789abcdef0123456789abcdef/messages" \
  -H "Authorization: Bearer $CHECKFU_PRINCIPAL_CREDENTIAL" \
  -H "Content-Type: application/json" \
  --data '{"message_id":"partner-message-1","text":"Review the repository and report the result."}'
```

| Field | Required | Meaning |
| - | - | - |
| `message_id` | yes | Your id for this message: 1 to 256 characters of `A-Z a-z 0-9 . _ : -`. Ids are scoped to the authenticated caller. |
| `text` | yes | The message text. |
| `session_id` | no | Continue a Session (`sess_...`) this caller opened through the same placement while that Session waits for input. Without it, the message opens its own Session. |
| `wait` | no | `false` answers as soon as the message is accepted. By default the response waits until the work stops running. |

The response is the message's current state:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "type": "agent_placement_message",
  "agent_placement_id": "aplace_0123456789abcdef0123456789abcdef",
  "message_id": "partner-message-1",
  "session_id": "sess_0123456789abcdef0123456789abcdef",
  "status": "completed",
  "answers": [
    { "event_id": "evt_0123456789abcdef0123456789abcdef", "text": "The repository passes its checks." }
  ],
  "updated_at": "2026-10-31T12:00:00.000Z"
}
```

| `status` | Meaning |
| - | - |
| `submitted`, `working` | The work is still running. |
| `input_required`, `auth_required` | The work waits on you or on a reviewer. Answer with `session_id`. |
| `completed`, `failed`, `canceled`, `rejected` | Final. |

An autonomous placement returns only this caller's own status and answers. A delegated placement returns the Session view its observers see.

## Publish an output contract

An `autonomous` placement can publish the shape of its answer as a Draft-07 JSON Schema, set as `output_contract` on `POST /v1/agent-placements` or `PATCH /v1/agent-placements/{agent_placement_id}`:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "output_contract": {
    "kind": "json_schema",
    "schema": {
      "type": "object",
      "properties": { "refund_cents": { "type": "integer", "minimum": 0 } },
      "required": ["refund_cents"],
      "additionalProperties": false
    }
  }
}
```

The schema follows the same bounded policy as Automation JSON output (at most 8 KiB; `pattern`, `patternProperties`, `format`, `uniqueItems`, `multipleOf`, `dependencies` and recursive references are refused). A `delegated` placement cannot publish one.

Each Session the placement opens freezes the contract it had when the Session was admitted. After you publish, replace or remove a contract, a Session opened under the earlier one is not continued: a message with its `session_id` is refused, and the caller sends a new `message_id` without `session_id`, which is admitted under the current contract. Under a contract, `answers` is always empty and the response carries `output` instead:

* `output` is `null` while the work runs, or when it stops `failed` or `canceled`;
* `output` is `{ "event_id": "evt_...", "value": { ... } }` once the work completes and the Agent's final message is JSON that satisfies the schema;
* `status` is `failed` and `output` is `null` when the final message is not JSON, breaks the schema, or there is none. The answer's text is never sent to the caller.

## Replay by `message_id`

Re-sending a `message_id` you already sent replays the same Session and its current result. It never accepts a second input, so retrying after a timeout or a dropped connection is safe. Re-send with `wait: false` to read where an earlier message stands without waiting.

## Cancel your own message

`POST /v1/agent-placements/{id}/messages/{message_id}/cancel` cancels one message you sent to an autonomous placement, named by your own `message_id`, and nothing else. It needs the same credential and `invoke` grant as sending, and no request body:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  "https://api.checkfu.com/v1/agent-placements/aplace_0123456789abcdef0123456789abcdef/messages/partner-message-1/cancel" \
  -H "Authorization: Bearer $CHECKFU_PRINCIPAL_CREDENTIAL"
```

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "type": "agent_placement_message_cancellation",
  "agent_placement_id": "aplace_0123456789abcdef0123456789abcdef",
  "message_id": "partner-message-1",
  "session_id": "sess_0123456789abcdef0123456789abcdef",
  "state": "canceled"
}
```

| `state` | Meaning |
| - | - |
| `withdrawn` | The message was still queued. It will never run. |
| `canceled` | Its turn held only this message and was stopped. |
| `completed`, `failed`, `ended` | The message had already settled. Nothing changed. |

A cancel never interrupts the whole Session or another caller's work. A message whose turn also holds other input, or waits on something no exact cancel can stop, answers `409 runtime.invalid_transition` and nothing changes. Another caller's `message_id` answers `404` exactly like one never sent, and so does any message at a delegated placement. Cancelling again answers the same state, and re-sending a cancelled `message_id` replays it as `canceled` with no answers.

## Refusals

Refusals use the standard [error envelope](https://docs.checkfu.com/reference/errors.md):

| HTTP | `error.type` | When |
| - | - | - |
| 400 | `validation.malformed` | The body is not JSON matching the fields above, or `Content-Type` is not `application/json`. |
| 401 | `auth.invalid_key` | The credential is missing, revoked, or not an `api`-audience Principal credential. |
| 403 | `policy.denied` | The caller lacks `invoke` on the placement, or the installation is deactivated. |
| 404 | `validation.not_found` | The placement is not active, or `session_id` names a Session this caller cannot continue through it. |
| 409 | `runtime.invalid_transition` | The Session cannot accept the message in its current state. |
| 413 | `validation.payload_too_large` | The body exceeds 1 MiB. |
| 422 | `runtime.harness_incompatible` | The Agent's configuration promises a guarantee its runtime cannot enforce. Nothing was started. |
| 500 | `runtime.internal` | The message could not be completed. Retry with the same `message_id`. |

## From the TypeScript SDK

`createPlacementCaller` from `@checkfu/sdk` sends the same request with the Principal credential. It never uses an API key:

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

const placement = createPlacementCaller({
  placement: "aplace_0123456789abcdef0123456789abcdef",
  credential: process.env.CHECKFU_PRINCIPAL_CREDENTIAL!,
})

// Keep the messageId: re-sending it replays the original Session, not new work.
const result = await placement.send({
  messageId: "partner-message-1",
  text: "Review the repository and report the result.",
})
```

`placement.cancelOwn("partner-message-1")` cancels that message and returns the cancellation above. A refusal raises `PlacementCallRefused` with the HTTP `status` and the error `type`.

To permanently delete a Session a placement message opened, call `DELETE /v1/sessions/{id}` with a Workspace API key:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request DELETE \
  "https://api.checkfu.com/v1/sessions/sess_0123456789abcdef0123456789abcdef" \
  -H "Authorization: Bearer $CHECKFU_API_KEY" \
  -H "Checkfu-Version: 2026-08-31" \
  -H "Idempotency-Key: erase-partner-session-1"
```

> [!WARNING]
>
> Use service entries for calls from outside your Workspace. To coordinate multiple Checkfu Agents inside the same Workspace, compose them in an [Automation](https://docs.checkfu.com/concepts/automations.md).


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