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

# Get access and identities

> Where your API key, Workspace, and Principals come from: the three things every other page assumes you have.

Every request in these docs assumes an **API key** bound to one **Workspace** and a **Principal ID**. This page is where they come from.

## Your first key and Workspace

Checkfu is in private alpha; access is by invitation. [Request access](https://checkfu.com/#early-access); an admitted Organization receives an **organization-scoped root API key** and at least one Workspace. That root key is the bootstrap credential. Everything below is created with it.

A key has a **role** (`root`, `admin`, `developer`, `runner`, or `viewer`) and a **scope**. Organization-scoped root keys manage the Organization. Ordinary API keys are hard-bound to one Workspace, matching CMA, and cannot select another Workspace by header. The full contract is in [Authentication](/reference/authentication).

## Mint the keys you'll actually ship with

Do not embed the root key in services. Create narrower keys for each system:

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl --request POST "https://api.checkfu.com/v1/organizations/$CHECKFU_ORGANIZATION_ID/api-keys" \
    --header "Authorization: Bearer $CHECKFU_ROOT_API_KEY" \
    --header "Checkfu-Version: 2026-08-27" \
    --header "Idempotency-Key: key-backend-$(uuidgen)" \
    --header "Content-Type: application/json" \
    --data '{
      "display_name": "backend-production",
      "role": "developer",
      "scope": { "type": "workspace", "workspace_id": "wrkspc_0123456789abcdef0123456789abcdef" },
      "rate_limit_per_minute": 600
    }'
  ```

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

  // Key management is an Organization-level surface. The Workspace the new key
  // binds to travels in `scope`.
  const root = new Checkfu({ apiKey: process.env.CHECKFU_ROOT_API_KEY })

  const issued = await root.apiKeys.create(
    process.env.CHECKFU_ORGANIZATION_ID,
    {
      display_name: "backend-production",
      role: "developer",
      scope: { type: "workspace", workspace_id: "wrkspc_0123456789abcdef0123456789abcdef" },
      rate_limit_per_minute: 600,
    },
    { idempotencyKey: `key-backend-${crypto.randomUUID()}` },
  )
  ```
</CodeGroup>

The secret is returned on create or rotate and may be replayed by an exact keyed retry for up to 24 hours only while that exact key generation remains live. `GET /v1/organizations/{organization_id}/api-keys` and `GET /v1/api-keys/{id}` return current key metadata but never a secret. Patch, rotation, revocation, or replay expiry makes an old retry fail closed without returning the secret. Only an organization-scoped `root` key can list, retrieve, create, rotate, re-role, or revoke keys. Creation and rotation require an `Idempotency-Key`: one stable value per intended credential, a new value per intentionally distinct one.

Key management is an **Organization-level** surface. The Workspace a new key is bound to travels in the body's `scope`, never a selector header.

## Principals: the people and services your agents act for

A **Principal** is an identity inside a Workspace: one of your end users, or one of your services. Every Session names the Principal it runs for, so before the [quickstart](/getting-started/quickstart) you need at least one:

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  curl --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: prin-vivian-$(uuidgen)" \
    --header "Content-Type: application/json" \
    --data '{
      "external_subject_id": "user-vivian@example.com",
      "principal_type": "person",
      "display_name": "Vivian"
    }'
  ```

  ```ts TypeScript theme={"theme":{"light":"github-light","dark":"github-dark"}}
  // `checkfu` uses a Workspace-bound API key. This identity-administration
  // extension names the same Workspace explicitly in its path.
  const principal = await checkfu.principals.create(
  	process.env.CHECKFU_WORKSPACE_ID,
  	{
      external_subject_id: "user-vivian@example.com",
      principal_type: "person",
      display_name: "Vivian",
    },
    { idempotencyKey: `prin-vivian-${crypto.randomUUID()}` },
  )
  ```
</CodeGroup>

* `external_subject_id` is **your** identifier for this identity: a user ID from your own database. Checkfu never authenticates your end users; your backend authenticates them and asserts the matching Principal on each request.
* `principal_type` is `person` for humans and `service` for machine identities (automations and installed agents execute as `service` Principals).

The returned `prin_…` is what you pass as `principal` when creating Sessions, and what [PermissionAssignments](/concepts/tenancy-and-governance) and [Budgets](/guides/attribute-usage) attach to. Create one Principal per end user. Per-user attribution, budgets, and audit all key off it.

## The three variables, ready

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
export CHECKFU_API_KEY="..."        # from onboarding, or a key you minted above
export CHECKFU_ORGANIZATION_ID="org_…"
export CHECKFU_WORKSPACE_ID="wrkspc_…"
export CHECKFU_PRINCIPAL_ID="prin_…"
```

The SDK reads only the API key and optional base URL. `CHECKFU_WORKSPACE_ID` is used when an Organization-management or identity-administration path names the Workspace explicitly; it is never a client selector.

Continue to the [quickstart](/getting-started/quickstart).
