From 383b4d8d26261984f1450ba2c81d026fddef644c Mon Sep 17 00:00:00 2001
From: Ilyaas Kapadia <86218345+IlyaasK@users.noreply.github.com>
Date: Thu, 28 May 2026 15:03:59 -0400
Subject: [PATCH 1/4] docs: add API key management guides
---
docs.json | 4 +-
info/api-keys.mdx | 217 +++++++++++++++++++++++++++++++++++++
info/projects.mdx | 2 +-
reference/cli.mdx | 3 +
reference/cli/api-keys.mdx | 83 ++++++++++++++
reference/cli/auth.mdx | 5 +-
6 files changed, 309 insertions(+), 5 deletions(-)
create mode 100644 info/api-keys.mdx
create mode 100644 reference/cli/api-keys.mdx
diff --git a/docs.json b/docs.json
index 763e3d5..2926831 100644
--- a/docs.json
+++ b/docs.json
@@ -84,7 +84,8 @@
"browsers/termination",
"browsers/standby",
"browsers/headless",
- "info/projects"
+ "info/projects",
+ "info/api-keys"
]
},
{
@@ -256,6 +257,7 @@
"reference/cli/browsers",
"reference/cli/apps",
"reference/cli/projects",
+ "reference/cli/api-keys",
"reference/cli/mcp",
"reference/cli/extensions"
]
diff --git a/info/api-keys.mdx b/info/api-keys.mdx
new file mode 100644
index 0000000..96fe162
--- /dev/null
+++ b/info/api-keys.mdx
@@ -0,0 +1,217 @@
+---
+title: "API Keys"
+description: "Create, scope, rotate, and delete Kernel API keys"
+---
+
+An API key is the credential your server, script, or CI job uses to call Kernel without an interactive login. Treat it like a password: keep it out of client-side code, store it in a secret manager, and rotate it when access changes.
+
+Kernel only returns the plaintext key once, when you create it. Save the `key` value immediately. After that, Kernel only shows the masked value.
+
+## Before you start
+
+You need one existing Kernel credential to create another API key:
+
+- Use `kernel login` for CLI-based creation.
+- Use `KERNEL_API_KEY` for SDK or REST-based creation.
+- Use `@onkernel/sdk>=0.58.0` or `kernel>=0.58.0` for SDK API key methods.
+
+API keys can be **org-wide** or **project-scoped**:
+
+- Omit `project_id` to create an org-wide key that can access resources across your organization.
+- Set `project_id` to create a key that can only access resources in that project.
+- When you authenticate with a project-scoped key, you can only create another project-scoped key for the same project.
+
+## Create an API key
+
+Use the CLI when you're creating a key from your terminal. Use the SDK or REST API when your backend needs to provision keys for environments, customers, or automation jobs.
+
+### CLI
+
+```bash
+kernel api-keys create \
+ --name staging-ci \
+ --days-to-expire 30 \
+ --project-id proj_staging_9f3k \
+ --output json
+```
+
+The command prints the plaintext `key` once. Store it in your secret manager before closing the terminal.
+
+
+ `--project-id` controls the access scope of the new API key. The global `--project` flag only scopes the CLI request you're making.
+
+
+### SDKs
+
+
+```typescript TypeScript
+import Kernel from '@onkernel/sdk';
+
+const kernel = new Kernel({
+ apiKey: process.env.KERNEL_API_KEY,
+});
+
+const apiKey = await kernel.apiKeys.create({
+ name: 'staging-ci',
+ days_to_expire: 30,
+ project_id: 'proj_staging_9f3k',
+});
+
+console.log(apiKey.key); // Save this value now. Kernel won't show it again.
+console.log(apiKey.id, apiKey.masked_key);
+```
+
+```python Python
+import os
+from kernel import Kernel
+
+client = Kernel(api_key=os.environ["KERNEL_API_KEY"])
+
+api_key = client.api_keys.create(
+ name="staging-ci",
+ days_to_expire=30,
+ project_id="proj_staging_9f3k",
+)
+
+print(api_key.key) # Save this value now. Kernel won't show it again.
+print(api_key.id, api_key.masked_key)
+```
+
+
+### REST API
+
+
+```typescript TypeScript
+const response = await fetch('https://api.onkernel.com/org/api_keys', {
+ method: 'POST',
+ headers: {
+ Authorization: `Bearer ${process.env.KERNEL_API_KEY}`,
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ name: 'staging-ci',
+ days_to_expire: 30,
+ project_id: 'proj_staging_9f3k',
+ }),
+});
+
+if (!response.ok) {
+ throw new Error(`Kernel returned ${response.status}`);
+}
+
+const apiKey = await response.json();
+console.log(apiKey.key);
+```
+
+```python Python
+import os
+import httpx
+
+response = httpx.post(
+ "https://api.onkernel.com/org/api_keys",
+ headers={
+ "Authorization": f"Bearer {os.environ['KERNEL_API_KEY']}",
+ "Content-Type": "application/json",
+ },
+ json={
+ "name": "staging-ci",
+ "days_to_expire": 30,
+ "project_id": "proj_staging_9f3k",
+ },
+)
+response.raise_for_status()
+
+api_key = response.json()
+print(api_key["key"])
+```
+
+
+## List and inspect API keys
+
+List keys to audit what exists. List and retrieve responses include `masked_key`, `project_id`, `project_name`, `created_by`, and expiry metadata, but they don't include the plaintext key.
+
+
+```typescript TypeScript
+for await (const apiKey of kernel.apiKeys.list({ limit: 20 })) {
+ console.log(apiKey.id, apiKey.name, apiKey.masked_key);
+}
+
+const apiKey = await kernel.apiKeys.retrieve('key_01jwv4tn5m8k3q2v7x9p0a1bc2');
+console.log(apiKey.project_id, apiKey.expires_at);
+```
+
+```python Python
+import os
+from kernel import Kernel
+
+client = Kernel(api_key=os.environ["KERNEL_API_KEY"])
+
+for api_key in client.api_keys.list(limit=20):
+ print(api_key.id, api_key.name, api_key.masked_key)
+
+api_key = client.api_keys.retrieve("key_01jwv4tn5m8k3q2v7x9p0a1bc2")
+print(api_key.project_id, api_key.expires_at)
+```
+
+
+You can do the same from the CLI:
+
+```bash
+kernel api-keys list
+kernel api-keys get key_01jwv4tn5m8k3q2v7x9p0a1bc2
+```
+
+## Rename or delete an API key
+
+Rename a key when the owner or purpose changes. Delete a key when the workload no longer needs access.
+
+
+```typescript TypeScript
+await kernel.apiKeys.update('key_01jwv4tn5m8k3q2v7x9p0a1bc2', {
+ name: 'staging-ci-rotated',
+});
+
+await kernel.apiKeys.delete('key_01jwv4tn5m8k3q2v7x9p0a1bc2');
+```
+
+```python Python
+import os
+from kernel import Kernel
+
+client = Kernel(api_key=os.environ["KERNEL_API_KEY"])
+
+client.api_keys.update(
+ "key_01jwv4tn5m8k3q2v7x9p0a1bc2",
+ name="staging-ci-rotated",
+)
+
+client.api_keys.delete("key_01jwv4tn5m8k3q2v7x9p0a1bc2")
+```
+
+
+Or use the CLI:
+
+```bash
+kernel api-keys update key_01jwv4tn5m8k3q2v7x9p0a1bc2 --name staging-ci-rotated
+kernel api-keys delete key_01jwv4tn5m8k3q2v7x9p0a1bc2 --yes
+```
+
+## Rotate a key
+
+Rotate by creating the replacement first, then deleting the old key after your workload has switched over.
+
+1. Create a new API key with the same scope.
+2. Store the new plaintext key in your secret manager.
+3. Deploy or restart the workload that uses `KERNEL_API_KEY`.
+4. Verify the workload can call Kernel.
+5. Delete the old API key.
+
+This keeps downtime low because the old key continues working until the new key is active everywhere.
+
+## Troubleshooting
+
+| Error | What it means | What to do |
+| --- | --- | --- |
+| `400 Bad Request` | The name is missing, `days_to_expire` is outside `1`-`3650`, or `project_id` is empty. | Send a name, choose a valid expiry, or omit `project_id` for an org-wide key. |
+| `401 Unauthorized` | Kernel couldn't authenticate the request. | Run `kernel login`, or set a valid `KERNEL_API_KEY`. |
+| `404 Not Found` | The project doesn't exist or the caller can't access it. | Check the project ID. If you're using a project-scoped key, create keys only for that same project. |
diff --git a/info/projects.mdx b/info/projects.mdx
index 2a42164..9693db7 100644
--- a/info/projects.mdx
+++ b/info/projects.mdx
@@ -79,7 +79,7 @@ other = kernel.browsers.create(
API keys can be **org-wide** or **project-scoped**.
- **Existing API keys are org-wide.** They see every resource in your organization across all projects. Include an `X-Kernel-Project-Id` header to restrict a single request to one project.
-- **Project-scoped API keys** can only access resources inside the project they were issued for. Create one from the **API Keys** page in the dashboard and pick the target project when generating the key. Requests made with a scoped key are automatically limited to that project — no header required. If you do send an `X-Kernel-Project-Id` header and it conflicts with the key's project, the request is rejected with `403 Forbidden`.
+- **Project-scoped API keys** can only access resources inside the project they were issued for. Create one from the **API Keys** page in the dashboard, the [CLI](/reference/cli/api-keys), an SDK, or the [API keys guide](/info/api-keys), and pass the target `project_id` when generating the key. Requests made with a scoped key are automatically limited to that project — no header required. If you do send an `X-Kernel-Project-Id` header and it conflicts with the key's project, the request is rejected with `403 Forbidden`.
### OAuth
diff --git a/reference/cli.mdx b/reference/cli.mdx
index 5c3c4f4..edcb503 100644
--- a/reference/cli.mdx
+++ b/reference/cli.mdx
@@ -46,6 +46,9 @@ kernel --version
Manage projects and scope commands with `--project`.
+
+ Create, list, rename, and delete API keys.
+
## Quick Start
diff --git a/reference/cli/api-keys.mdx b/reference/cli/api-keys.mdx
new file mode 100644
index 0000000..f1e0ce3
--- /dev/null
+++ b/reference/cli/api-keys.mdx
@@ -0,0 +1,83 @@
+---
+title: "API Keys"
+---
+
+Manage [API keys](/info/api-keys) from the CLI.
+
+## `kernel api-keys create`
+
+Create an API key. By default, the new key is org-wide. Pass `--project-id` to create a key whose own access is scoped to that project.
+
+```bash
+kernel api-keys create \
+ --name staging-ci \
+ --days-to-expire 30 \
+ --project-id proj_staging_9f3k \
+ --output json
+```
+
+| Flag | Description |
+|------|-------------|
+| `--name ` | API key name. Required. |
+| `--days-to-expire ` | Number of days until expiry, from `1` to `3650`. Omit for no expiry. |
+| `--project-id ` | Create a project-scoped API key for this project. Omit for org-wide. |
+| `--output json`, `-o json` | Output the raw JSON object, including the plaintext `key` on create. |
+
+
+ `--project-id` controls the access scope of the new API key. The global `--project` flag only scopes the CLI request you're making.
+
+
+## `kernel api-keys list`
+
+List API keys in the authenticated organization. API keys are masked.
+
+```bash
+kernel api-keys list --limit 20
+```
+
+| Flag | Description |
+|------|-------------|
+| `--limit ` | Maximum number of results to return. |
+| `--offset ` | Number of results to skip. |
+| `--output json`, `-o json` | Output the raw JSON array. |
+
+## `kernel api-keys get `
+
+Show one API key by ID. The response includes the masked key and metadata, not the plaintext key.
+
+```bash
+kernel api-keys get key_01jwv4tn5m8k3q2v7x9p0a1bc2
+```
+
+| Flag | Description |
+|------|-------------|
+| `--output json`, `-o json` | Output the raw JSON object. |
+
+## `kernel api-keys update `
+
+Rename an API key.
+
+```bash
+kernel api-keys update key_01jwv4tn5m8k3q2v7x9p0a1bc2 --name staging-ci-rotated
+```
+
+| Flag | Description |
+|------|-------------|
+| `--name ` | New API key name. Required. |
+| `--output json`, `-o json` | Output the raw JSON object. |
+
+## `kernel api-keys delete `
+
+Delete an API key.
+
+```bash
+kernel api-keys delete key_01jwv4tn5m8k3q2v7x9p0a1bc2 --yes
+```
+
+| Flag | Description |
+|------|-------------|
+| `--yes`, `-y` | Skip the confirmation prompt. |
+
+## Aliases
+
+You can also use `kernel api-key`, `kernel apikeys`, or `kernel apikey`.
diff --git a/reference/cli/auth.mdx b/reference/cli/auth.mdx
index 12a889c..23b2771 100644
--- a/reference/cli/auth.mdx
+++ b/reference/cli/auth.mdx
@@ -19,10 +19,10 @@ Display authentication status, including the active user, organization, and toke
Set the `KERNEL_API_KEY` environment variable to authenticate without OAuth:
```bash
-export KERNEL_API_KEY=
+export KERNEL_API_KEY=sk_1234abcd
```
-Create and manage API keys from the Kernel dashboard.
+Create and manage API keys from the Kernel dashboard or with [`kernel api-keys`](/reference/cli/api-keys).
## Global flags
The following flags are available on every CLI command:
@@ -36,4 +36,3 @@ The following flags are available on every CLI command:
## Getting help
- `kernel --help` — Show the top-level command list.
- `kernel --help` — Display command-specific usage and options.
-
From cd302a5e27884f00d5a5ff7a05da517d9b0aeef2 Mon Sep 17 00:00:00 2001
From: Ilyaas Kapadia <86218345+IlyaasK@users.noreply.github.com>
Date: Thu, 28 May 2026 15:22:21 -0400
Subject: [PATCH 2/4] docs: link API key guide to CLI reference
---
info/api-keys.mdx | 18 +++---------------
1 file changed, 3 insertions(+), 15 deletions(-)
diff --git a/info/api-keys.mdx b/info/api-keys.mdx
index 96fe162..585ba5a 100644
--- a/info/api-keys.mdx
+++ b/info/api-keys.mdx
@@ -37,9 +37,7 @@ kernel api-keys create \
The command prints the plaintext `key` once. Store it in your secret manager before closing the terminal.
-
- `--project-id` controls the access scope of the new API key. The global `--project` flag only scopes the CLI request you're making.
-
+The `--project-id` flag scopes the new API key itself. For every `kernel api-keys` command, flag, output option, alias, and the difference between `--project-id` and the global `--project` flag, see the [API keys CLI reference](/reference/cli/api-keys).
### SDKs
@@ -154,12 +152,7 @@ print(api_key.project_id, api_key.expires_at)
```
-You can do the same from the CLI:
-
-```bash
-kernel api-keys list
-kernel api-keys get key_01jwv4tn5m8k3q2v7x9p0a1bc2
-```
+You can also list and inspect keys from the CLI. See the [API keys CLI reference](/reference/cli/api-keys) for commands and flags.
## Rename or delete an API key
@@ -189,12 +182,7 @@ client.api_keys.delete("key_01jwv4tn5m8k3q2v7x9p0a1bc2")
```
-Or use the CLI:
-
-```bash
-kernel api-keys update key_01jwv4tn5m8k3q2v7x9p0a1bc2 --name staging-ci-rotated
-kernel api-keys delete key_01jwv4tn5m8k3q2v7x9p0a1bc2 --yes
-```
+You can also rename and delete keys from the CLI. See the [API keys CLI reference](/reference/cli/api-keys) for commands and flags.
## Rotate a key
From 3eca23f92b2edf963565e6cd88ea07c7ec4efb2c Mon Sep 17 00:00:00 2001
From: Ilyaas Yusuf Kapadia <86218345+IlyaasK@users.noreply.github.com>
Date: Fri, 29 May 2026 11:44:49 -0400
Subject: [PATCH 3/4] Update info/api-keys.mdx
Co-authored-by: Eric Feng
---
info/api-keys.mdx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/info/api-keys.mdx b/info/api-keys.mdx
index 585ba5a..05c39f3 100644
--- a/info/api-keys.mdx
+++ b/info/api-keys.mdx
@@ -15,10 +15,10 @@ You need one existing Kernel credential to create another API key:
- Use `KERNEL_API_KEY` for SDK or REST-based creation.
- Use `@onkernel/sdk>=0.58.0` or `kernel>=0.58.0` for SDK API key methods.
-API keys can be **org-wide** or **project-scoped**:
+API keys can be **org** or **project** scoped:
-- Omit `project_id` to create an org-wide key that can access resources across your organization.
-- Set `project_id` to create a key that can only access resources in that project.
+- Omit `project_id` to create an org-scoped key that can access resources across your organization.
+- Set `project_id` to create a project-scoped key that can only access resources in that project.
- When you authenticate with a project-scoped key, you can only create another project-scoped key for the same project.
## Create an API key
From f975c575fd9cc1fa0075c556663fb94ab5b7b57b Mon Sep 17 00:00:00 2001
From: Ilyaas Kapadia <86218345+IlyaasK@users.noreply.github.com>
Date: Fri, 29 May 2026 11:52:15 -0400
Subject: [PATCH 4/4] Address API key docs review feedback
---
docs.json | 4 +--
info/api-keys.mdx | 78 +++--------------------------------------------
2 files changed, 6 insertions(+), 76 deletions(-)
diff --git a/docs.json b/docs.json
index 2926831..5991021 100644
--- a/docs.json
+++ b/docs.json
@@ -84,8 +84,7 @@
"browsers/termination",
"browsers/standby",
"browsers/headless",
- "info/projects",
- "info/api-keys"
+ "info/projects"
]
},
{
@@ -114,6 +113,7 @@
"auth/faq"
]
},
+ "info/api-keys",
"browsers/file-io",
"browsers/curl",
"browsers/ssh",
diff --git a/info/api-keys.mdx b/info/api-keys.mdx
index 05c39f3..20194ed 100644
--- a/info/api-keys.mdx
+++ b/info/api-keys.mdx
@@ -11,9 +11,7 @@ Kernel only returns the plaintext key once, when you create it. Save the `key` v
You need one existing Kernel credential to create another API key:
-- Use `kernel login` for CLI-based creation.
-- Use `KERNEL_API_KEY` for SDK or REST-based creation.
-- Use `@onkernel/sdk>=0.58.0` or `kernel>=0.58.0` for SDK API key methods.
+- Set `KERNEL_API_KEY` before running the SDK examples.
API keys can be **org** or **project** scoped:
@@ -23,21 +21,7 @@ API keys can be **org** or **project** scoped:
## Create an API key
-Use the CLI when you're creating a key from your terminal. Use the SDK or REST API when your backend needs to provision keys for environments, customers, or automation jobs.
-
-### CLI
-
-```bash
-kernel api-keys create \
- --name staging-ci \
- --days-to-expire 30 \
- --project-id proj_staging_9f3k \
- --output json
-```
-
-The command prints the plaintext `key` once. Store it in your secret manager before closing the terminal.
-
-The `--project-id` flag scopes the new API key itself. For every `kernel api-keys` command, flag, output option, alias, and the difference between `--project-id` and the global `--project` flag, see the [API keys CLI reference](/reference/cli/api-keys).
+Use the SDKs when your backend needs to provision keys for environments, customers, or automation jobs.
### SDKs
@@ -76,54 +60,6 @@ print(api_key.id, api_key.masked_key)
```
-### REST API
-
-
-```typescript TypeScript
-const response = await fetch('https://api.onkernel.com/org/api_keys', {
- method: 'POST',
- headers: {
- Authorization: `Bearer ${process.env.KERNEL_API_KEY}`,
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- name: 'staging-ci',
- days_to_expire: 30,
- project_id: 'proj_staging_9f3k',
- }),
-});
-
-if (!response.ok) {
- throw new Error(`Kernel returned ${response.status}`);
-}
-
-const apiKey = await response.json();
-console.log(apiKey.key);
-```
-
-```python Python
-import os
-import httpx
-
-response = httpx.post(
- "https://api.onkernel.com/org/api_keys",
- headers={
- "Authorization": f"Bearer {os.environ['KERNEL_API_KEY']}",
- "Content-Type": "application/json",
- },
- json={
- "name": "staging-ci",
- "days_to_expire": 30,
- "project_id": "proj_staging_9f3k",
- },
-)
-response.raise_for_status()
-
-api_key = response.json()
-print(api_key["key"])
-```
-
-
## List and inspect API keys
List keys to audit what exists. List and retrieve responses include `masked_key`, `project_id`, `project_name`, `created_by`, and expiry metadata, but they don't include the plaintext key.
@@ -152,8 +88,6 @@ print(api_key.project_id, api_key.expires_at)
```
-You can also list and inspect keys from the CLI. See the [API keys CLI reference](/reference/cli/api-keys) for commands and flags.
-
## Rename or delete an API key
Rename a key when the owner or purpose changes. Delete a key when the workload no longer needs access.
@@ -182,8 +116,6 @@ client.api_keys.delete("key_01jwv4tn5m8k3q2v7x9p0a1bc2")
```
-You can also rename and delete keys from the CLI. See the [API keys CLI reference](/reference/cli/api-keys) for commands and flags.
-
## Rotate a key
Rotate by creating the replacement first, then deleting the old key after your workload has switched over.
@@ -194,12 +126,10 @@ Rotate by creating the replacement first, then deleting the old key after your w
4. Verify the workload can call Kernel.
5. Delete the old API key.
-This keeps downtime low because the old key continues working until the new key is active everywhere.
-
## Troubleshooting
| Error | What it means | What to do |
| --- | --- | --- |
-| `400 Bad Request` | The name is missing, `days_to_expire` is outside `1`-`3650`, or `project_id` is empty. | Send a name, choose a valid expiry, or omit `project_id` for an org-wide key. |
-| `401 Unauthorized` | Kernel couldn't authenticate the request. | Run `kernel login`, or set a valid `KERNEL_API_KEY`. |
+| `400 Bad Request` | The name is missing, `days_to_expire` is outside `1`-`3650`, or `project_id` is empty. | Send a name, choose a valid expiry, or omit `project_id` for an org-scoped key. |
+| `401 Unauthorized` | Kernel couldn't authenticate the request. | Set a valid `KERNEL_API_KEY`. |
| `404 Not Found` | The project doesn't exist or the caller can't access it. | Check the project ID. If you're using a project-scoped key, create keys only for that same project. |