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

# BYOK keys

> Bring your own LLM API key — used by scheduled runs and on-platform AI generation.

BYOK ("bring your own key") lets you paste an Anthropic or OpenAI API key into your workspace. The platform validates it, encrypts it, and uses it on your behalf for two specific flows:

* **Scheduled runs** — the cron-driven agent loop that calls `generateText` against the configured provider.
* **`blocks.create`** — the AI block generator that streams a new block from a prompt.

BYOK keys are the substrate for any LLM call billed to *you*, not the platform.

<Note>
  BYOK keys are a **Builder beta** feature. Free workspaces can read this page, but `/settings/api-keys` shows an upgrade panel and MCP `byokKeys.*` writes require Builder.
</Note>

## Why BYOK

Two reasons:

1. **Your provider, your billing.** Every LLM call is metered against your Anthropic or OpenAI account. The platform doesn't mark up tokens.
2. **Your model choice.** You pick the model per scheduled run from the supported list, and you can swap to a cheaper model without us pushing a release.

BYOK keys are gated while in beta so provider-key handling stays on the paid, higher-intent path.

## Add a key

<Steps>
  <Step title="Go to API keys">
    `/settings/api-keys` → BYOK panel. Scheduled runs and MCP AI generation are labeled **Beta** in settings; Free workspaces see an upsell to `/settings/billing`.
  </Step>

  <Step title="Paste the API key">
    Anthropic (`sk-ant-*`) or OpenAI (`sk-*` or `sk-proj-*`). Optionally pick a label so you can tell multiple keys apart.

    The platform validates against the provider's `/v1/models` endpoint with a 5-second timeout. Validation failures bubble up with a clear message — "Provider rejected the key" on 401/403, "Could not reach \<provider>" on network errors.
  </Step>

  <Step title="Save">
    The plaintext is envelope-encrypted with your workspace DEK. The list view shows the last 4 characters (after stripping known prefixes like `sk-ant-api01-`, `sk-proj-`, etc.) plus the provider and label — never the full key.
  </Step>
</Steps>

## Supported providers and models

Two providers in v1. Each carries a whitelist of model IDs the platform will accept at scheduled-run-set time.

<Tabs>
  <Tab title="Anthropic">
    | Model ID            |
    | ------------------- |
    | `claude-opus-4-7`   |
    | `claude-sonnet-4-6` |
    | `claude-haiku-4-5`  |

    The whitelist lives in `packages/shared/src/byokProviders.ts`. To add a model, open a PR there.
  </Tab>

  <Tab title="OpenAI">
    | Model ID       |
    | -------------- |
    | `gpt-5.5`      |
    | `gpt-5.4-mini` |
    | `gpt-5`        |
  </Tab>
</Tabs>

Picking a model outside the whitelist when creating a scheduled run is rejected with a clear error. Picking one inside the whitelist is forwarded to the provider via the AI SDK.

## How keys get used

The runner decrypts your key just-in-time and passes it directly to the provider — the plaintext never travels back out of the action.

<AccordionGroup>
  <Accordion title="Scheduled runs">
    `convex/scheduledRuns/runner.ts` looks up the BYOK row by id, calls `_decryptForRunner({ actor: "system:scheduledRunner" })`, and instantiates `@ai-sdk/anthropic` or `@ai-sdk/openai` with the plaintext as the API key.

    AI Gateway is intentionally skipped — gateway BYOK is project-scoped, so per-workspace BYOK has to use the provider SDKs directly.
  </Accordion>

  <Accordion title="schedule.preview">
    The streaming preview tool decrypts with `actor: "system:schedulePreview"` so preview events are distinguishable from production runs in the audit log.
  </Accordion>

  <Accordion title="blocks.create">
    The AI block generator uses a separate inference path (`@vercel/ai` `streamObject`) but the BYOK selection is the same — pick the key, pick the model, run.
  </Accordion>
</AccordionGroup>

Every decryption writes an `auditLog` row with the calling actor (`system:scheduledRunner`, `system:schedulePreview`, etc.), the BYOK key id, and the timestamp. Customers can see exactly when their key was used and by which flow.

## MCP tools

Three tools surface the BYOK panel to agents. Admin-only.

| Tool              | Purpose                                                           |
| ----------------- | ----------------------------------------------------------------- |
| `byokKeys.add`    | Add a key. Same validation as the UI. Audit-logged.               |
| `byokKeys.list`   | List keys (metadata only — never returns plaintext). Not audited. |
| `byokKeys.delete` | Remove a key by id. Audit-logged.                                 |

See the [MCP tool reference](/mcp/tool-reference) for the argument shapes.

## Encryption details

* **AES-256-GCM** with a per-workspace data encryption key (DEK).
* DEK is wrapped with a master key encryption key (KEK) held in Convex env (`KEK_V1`).
* Ciphertext is stored inline on the `customerApiKeys` row (vs. data sources, which store ciphertext in a separate table).
* `keyVersion` field tracks the KEK version for rotation.

KEK rotation is a future runbook (re-wrap every DEK, increment `kekVersion`). Until then, KEK is set once at platform install and stays put.

## What's next

<CardGroup cols={2}>
  <Card title="Scheduled runs" icon="clock" href="/scheduled-runs/concepts">
    Cron-driven agent loops powered by your BYOK key.
  </Card>

  <Card title="Audit log" icon="scroll" href="/concepts/audit-log">
    Every BYOK decrypt is logged with actor + key id + timestamp.
  </Card>
</CardGroup>
