Skip to content
kenari.

Per-tool keys

Create, list and revoke per-tool keys. A per-tool key is a normal kenari key that belongs to one tool on one machine, for example OpenCode on your laptop. If one tool’s config leaks, you revoke that one key and the rest keep working. The endpoints are specific to kenari and have no OpenAI or Anthropic equivalent.

POST /v1/account/keys, GET /v1/account/keys and DELETE /v1/account/keys/{id}

Only the key that kenari login saved can manage per-tool keys. See kenari CLI. Any other key gets 403 and cli_login_key_required, including:

  • A key you created under API Keys in the dashboard, and the key from kenari login --api-key.
  • A per-tool key. A tool cannot create more keys for itself.
  • A key from kenari login that you created before per-tool keys existed. Run kenari login again to get one that works.

A shared API key gets shared_key_not_allowed, and a restricted key gets restricted_key_not_allowed. A key is restricted when it has a model restriction, a spend cap or a token cap. See Account overview.

Each call needs the key in the Authorization header as Bearer kn-....

POST /v1/account/keys creates a key under the key you call it with.

FieldTypeRequiredDescription
toolstringYesThe tool the key is for, such as opencode. 1 to 40 characters of a-z, 0-9, . and -.
hoststringYesThe machine the tool runs on, such as laptop. 1 to 64 characters of A-Z, a-z, 0-9, ., _ and -.

The response is 201 with the new key:

{
"id": "e72755dd-ea6b-4d93-9e96-eb4c817824d0",
"key": "kn-984e1408144f2e604c38d27e6596638ec57882ad022a4fdd",
"prefix": "kn-984e1408",
"label": "kenari-cli · opencode · laptop",
"created_at": 1790000000
}
FieldTypeDescription
idstringThe id to use when you revoke the key.
keystringThe full key. It is shown once, so store it now.
prefixstringThe first characters of the key, to tell keys apart.
labelstringkenari-cli · <tool> · <host>. The same label shows next to the key under API Keys in the dashboard.
created_atintegerWhen the key was created, in epoch seconds.

A per-tool key works like the key it came from for every model and endpoint, except that it cannot manage keys. It follows the same billing as your other keys.

GET /v1/account/keys takes no parameters. It returns the active per-tool keys of the key you call it with, newest first. Revoked keys are not listed, and the full key is never returned again.

{
"object": "list",
"data": [
{
"id": "e72755dd-ea6b-4d93-9e96-eb4c817824d0",
"prefix": "kn-984e1408",
"label": "kenari-cli · opencode · laptop",
"created_at": 1790000000,
"last_used_at": 1790003600
}
]
}

last_used_at is when the key last made a request, in epoch seconds. It is null when no use is recorded in the last 90 days.

DELETE /v1/account/keys/{id} revokes one per-tool key for good. The next request that uses it gets a 401.

{
"id": "e72755dd-ea6b-4d93-9e96-eb4c817824d0",
"revoked": true
}

The id must belong to a per-tool key of the key you call it with. Any other id returns 404 and not_found, whether the key does not exist, is already revoked, or belongs to someone else.

  • One login key can hold 30 active per-tool keys. Revoke one to create another.
  • Per-tool keys count toward your account’s key limit, the same as any other key.
  • Each login key can create 20 keys per minute.

When you revoke the key from kenari login under API Keys in the dashboard, all its per-tool keys are revoked with it. Revoking a single per-tool key does not affect the login key or the other per-tool keys. See Authentication and API keys.

Terminal window
# Create a key
curl https://kenari.id/v1/account/keys \
-H "Authorization: Bearer $KENARI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool": "opencode",
"host": "laptop"
}'
# List keys
curl https://kenari.id/v1/account/keys \
-H "Authorization: Bearer $KENARI_API_KEY"
# Revoke a key
curl -X DELETE https://kenari.id/v1/account/keys/e72755dd-ea6b-4d93-9e96-eb4c817824d0 \
-H "Authorization: Bearer $KENARI_API_KEY"

Here $KENARI_API_KEY is the key from kenari login.

import os
import requests
headers = {"Authorization": f"Bearer {os.environ['KENARI_API_KEY']}"}
created = requests.post(
"https://kenari.id/v1/account/keys",
headers=headers,
json={"tool": "opencode", "host": "laptop"},
)
created.raise_for_status()
tool_key = created.json()
print(tool_key["label"])
requests.delete(
f"https://kenari.id/v1/account/keys/{tool_key['id']}",
headers=headers,
).raise_for_status()
const headers = { Authorization: `Bearer ${process.env.KENARI_API_KEY}` };
const created = await fetch("https://kenari.id/v1/account/keys", {
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ tool: "opencode", host: "laptop" }),
});
if (!created.ok) throw new Error(await created.text());
const toolKey = await created.json();
console.log(toolKey.label);
const revoked = await fetch(`https://kenari.id/v1/account/keys/${toolKey.id}`, {
method: "DELETE",
headers,
});
if (!revoked.ok) throw new Error(await revoked.text());

Creating, listing and revoking keys is free. Requests made with a per-tool key are billed like any other request. See How billing works.

StatusCodeWhen
400bad_requestThe body is not JSON with string fields tool and host, or one of them has characters or a length that is not allowed. Also when your account is at its key limit, with the message key limit reached.
401NoneThe key is missing, invalid, revoked or expired. The body is a short plain-text message, not JSON.
403cli_login_key_requiredThe key is not the one from kenari login.
403shared_key_not_allowedThe key is a shared API key.
403restricted_key_not_allowedThe key has a model restriction, a spend cap or a token cap.
404not_foundOn revoke, the id is not an active per-tool key of the key you called with.
409child_key_capOn create, the login key already holds 30 active per-tool keys.
429rate_limit_exceededOn create, more than 20 requests in one minute. There is no Retry-After header. Wait a few seconds and retry.
500internal_errorSomething failed in kenari. Retry once.

See Errors for the error format and the remaining codes.