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

# Scoped API Keys

> Create restricted API keys for frontends, integrations, and end users.

Your organization API key (`chd_org_...`) is the master key. It can do everything — create instances, delete them, manage billing. You absolutely should not put it in a frontend, a mobile app, or hand it to a third-party integration.

That's where scoped keys come in. A scoped key (`chd_sk_...`) is locked down to specific instances and specific permissions. It can only do what you allow.

## Why scoped keys?

* **Frontends**: Your chat UI only needs to send messages and read status. Give it `read` + `interact`, nothing else.
* **Integrations**: A Zapier workflow that installs skills? Give it `configure` on one instance.
* **End users**: Give each user a key scoped to their instance with `interact` only. They can chat but can't touch config.
* **Time-limited access**: Set `expires_at` and the key auto-revokes.

## Set up

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
export CHOWDER_KEY="chd_org_your_key_here"
export INSTANCE_ID="your-instance-id"
```

<Steps>
  ### Create a scoped key

  Let's create a key for a frontend chat widget that can only read instance info and send messages to one specific instance:

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl -X POST https://api.chowder.dev/v1/keys \
    -H "Authorization: Bearer $CHOWDER_KEY" \
    -H "Content-Type: application/json" \
    -d "{
      \"name\": \"frontend-chat\",
      \"instance_ids\": [\"$INSTANCE_ID\"],
      \"permissions\": [\"read\", \"interact\"]
    }"
  ```

  ```json Response theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "id": "b2f4e8a1-3c7d-4e9f-a5b6-1d8e3f7c2a94",
    "organization_id": "4737cccd-3d1d-4790-979b-6825d6a333de",
    "name": "frontend-chat",
    "key_prefix": "chd_sk_f3a8....",
    "is_active": true,
    "expires_at": null,
    "last_used_at": null,
    "created_at": "2026-02-14T12:00:00Z",
    "raw_key": "chd_sk_f3a8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0"
  }
  ```

  <Warning>
    The `raw_key` is only shown **once** — in this creation response. Copy it now. If you lose it, you'll need to create a new key. Chowder only stores a hash of the key, not the key itself.
  </Warning>

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  export SCOPED_KEY="chd_sk_f3a8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0"
  ```

  You can also set an expiration:

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl -X POST https://api.chowder.dev/v1/keys \
    -H "Authorization: Bearer $CHOWDER_KEY" \
    -H "Content-Type: application/json" \
    -d "{
      \"name\": \"temp-demo-key\",
      \"instance_ids\": [\"$INSTANCE_ID\"],
      \"permissions\": [\"read\", \"interact\"],
      \"expires_at\": \"2026-03-01T00:00:00Z\"
    }"
  ```

  The key will automatically stop working after the expiration date.

  ### Use the scoped key to send a message

  The scoped key works just like your org key for the permissions you've granted:

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl -X POST https://api.chowder.dev/v1/instances/$INSTANCE_ID/responses \
    -H "Authorization: Bearer $SCOPED_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "claude-sonnet-4-20250514",
      "input": "Hello from a scoped key!"
    }'
  ```

  ```json Response — works fine theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "id": "resp_7a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
    "status": "completed",
    "model": "claude-sonnet-4-20250514",
    "output": [
      {
        "type": "message",
        "role": "assistant",
        "content": [
          {
            "type": "output_text",
            "text": "Hey there! Message received loud and clear."
          }
        ]
      }
    ]
  }
  ```

  Reading instance info also works (we granted `read`):

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl https://api.chowder.dev/v1/instances/$INSTANCE_ID \
    -H "Authorization: Bearer $SCOPED_KEY"
  ```

  ### See it fail for admin operations

  Now try something outside the key's permissions — like stopping the instance:

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl -X POST https://api.chowder.dev/v1/instances/$INSTANCE_ID/stop \
    -H "Authorization: Bearer $SCOPED_KEY"
  ```

  ```json Response — 403 Forbidden theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "detail": "This endpoint requires an organization API key."
  }
  ```

  Or try accessing a different instance that the key isn't scoped to:

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl https://api.chowder.dev/v1/instances/some-other-instance-id \
    -H "Authorization: Bearer $SCOPED_KEY"
  ```

  ```json Response — 403 Forbidden theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "detail": "No access to this instance."
  }
  ```

  The key is locked down exactly as configured. It can only do `read` and `interact` on the instance you specified.

  ### List your keys

  See all scoped keys in your organization:

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl https://api.chowder.dev/v1/keys \
    -H "Authorization: Bearer $CHOWDER_KEY"
  ```

  ```json Response theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  [
    {
      "id": "b2f4e8a1-3c7d-4e9f-a5b6-1d8e3f7c2a94",
      "organization_id": "4737cccd-3d1d-4790-979b-6825d6a333de",
      "name": "frontend-chat",
      "key_prefix": "chd_sk_f3a8....",
      "is_active": true,
      "expires_at": null,
      "last_used_at": "2026-02-14T12:05:00Z",
      "created_at": "2026-02-14T12:00:00Z"
    }
  ]
  ```

  <Note>
    Notice `raw_key` is **not** in the list response — only the `key_prefix` for identification. Chowder never returns the full key after creation.
  </Note>

  Get detailed info on a specific key, including which instances and permissions it has:

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl https://api.chowder.dev/v1/keys/b2f4e8a1-3c7d-4e9f-a5b6-1d8e3f7c2a94 \
    -H "Authorization: Bearer $CHOWDER_KEY"
  ```

  ```json Response theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  {
    "id": "b2f4e8a1-3c7d-4e9f-a5b6-1d8e3f7c2a94",
    "organization_id": "4737cccd-3d1d-4790-979b-6825d6a333de",
    "name": "frontend-chat",
    "key_prefix": "chd_sk_f3a8....",
    "is_active": true,
    "expires_at": null,
    "last_used_at": "2026-02-14T12:05:00Z",
    "created_at": "2026-02-14T12:00:00Z",
    "instances": [
      {
        "instance_id": "ead7b76b-f34e-4b91-93e7-9c979cf9e41c",
        "permissions": ["read", "interact"]
      }
    ]
  }
  ```

  ### Revoke a key

  When a key is compromised or no longer needed:

  ```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl -X DELETE https://api.chowder.dev/v1/keys/b2f4e8a1-3c7d-4e9f-a5b6-1d8e3f7c2a94 \
    -H "Authorization: Bearer $CHOWDER_KEY"
  ```

  Returns `204 No Content`. The key is immediately deactivated. Any request using it will get a `403 Forbidden`.

  <Warning>
    Revoking a key is instant and permanent. Make sure you're ready — any frontend or integration using that key will break immediately.
  </Warning>
</Steps>

## Permission reference

Here's every permission you can assign to a scoped key:

| Permission  | What it grants                                                                               |
| ----------- | -------------------------------------------------------------------------------------------- |
| `read`      | View instance details and status (`GET /v1/instances/{id}`, `GET /v1/instances/{id}/status`) |
| `interact`  | Send messages and receive responses (`POST /v1/instances/{id}/responses`, session responses) |
| `configure` | Update instance config, manage skills (`PATCH /v1/instances/{id}`, skills endpoints)         |
| `files`     | Read, write, delete, and move files in the instance workspace                                |
| `channels`  | Connect, disconnect, and manage messaging channels                                           |

<Tip>
  For a typical chat frontend, `["read", "interact"]` is all you need. Start minimal and add permissions only when required.
</Tip>

## Multi-instance keys

A single scoped key can access multiple instances with the same permissions:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl -X POST https://api.chowder.dev/v1/keys \
  -H "Authorization: Bearer $CHOWDER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "dashboard-key",
    "instance_ids": [
      "ead7b76b-f34e-4b91-93e7-9c979cf9e41c",
      "c4a91d3e-8f2b-4e7a-b5c6-2d9f1e8a4b73",
      "f7e23b8d-1a4c-4d96-9e5f-3b7c2d8a1e64"
    ],
    "permissions": ["read", "interact", "configure"]
  }'
```

This is useful for admin dashboards that need to manage a fleet of instances.

## Common patterns

<Tabs>
  <Tab title="Chat frontend">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    {
      "name": "web-chat",
      "instance_ids": ["<instance-id>"],
      "permissions": ["read", "interact"]
    }
    ```

    Minimal access. Can read status and send messages. Can't change config, manage channels, or delete anything.
  </Tab>

  <Tab title="Admin dashboard">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    {
      "name": "admin-panel",
      "instance_ids": ["<id-1>", "<id-2>", "<id-3>"],
      "permissions": ["read", "interact", "configure", "files", "channels"]
    }
    ```

    Full access to specific instances, but still can't create/delete instances or manage billing (that requires the org key).
  </Tab>

  <Tab title="Temp demo access">
    ```json theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
    {
      "name": "demo-2026-02",
      "instance_ids": ["<instance-id>"],
      "permissions": ["read", "interact"],
      "expires_at": "2026-02-28T23:59:59Z"
    }
    ```

    Auto-expires at end of month. Great for demos or trial access.
  </Tab>
</Tabs>

## What's next?

<CardGroup cols={2}>
  <Card title="Build a Frontend" icon="browser" href="/guides/build-a-frontend">
    Put that scoped key to use in a Next.js chat interface.
  </Card>

  <Card title="Connect Telegram" icon="paper-plane" href="/guides/connect-telegram">
    Wire up a messaging channel to your agent.
  </Card>
</CardGroup>
