Skip to content

API keys

Included free on every install, up to five active keys per tenant and the last thirty days of each key's request history. A license with the apikey-pro feature lifts both and lets you set or raise a key's monthly request limit. See pricing.

An API key lets a program call the Content API (/api/v1/*) without a person signing in. Each key carries scopes that say what it may read and write, an optional expiry and an optional monthly request limit. Every request a key makes is recorded against it, refused ones included.

Open Access > API keys. A super admin sees every key in the tenant with its roles, scopes, status, expiry and Usage (this month). All, Active and Revoked narrow the list.

  1. Choose New key. Give it a Name, pick its Roles, and choose under Access what it may reach on each part of the Content API. The form lists the scopes it will grant.
  2. Set Expires and the Request limits if the key needs them, then choose Create.
  3. Copy the key from API key created. It is not shown again.

Each row has buttons to set the key's request limits, revoke it and delete it.

Access > API keys listing four keys with their roles, scopes, status and requests this month, one of them revoked.

CheckWhat passes
The keySent in X-API-Key, enabled and not expired. Anything else answers 401.
The scopeThe scope the method and path need is one the key holds.
The content typesA key with a schemas list reaches only those content types.
The roleA write to content needs the editor, admin or super_admin role on the key.
The access rulesThe key's roles go through access rules, the same rules as a person's. A read needs a rule that allows read on the content type, and a write one that allows the action. A field mask on a role hides those fields from the key too.
The monthly limitThe key has requests left this calendar month (UTC).

A scope is <resource>:<action>. The resource is the first path segment after /api/v1/, so /api/v1/content/post is content. The action comes from the method:

MethodAction
GET, HEAD, OPTIONSread
POST, PUT, PATCHwrite
DELETEdelete

content:read reads content. content:* allows every action on content, *:read reads everything, and *:* allows everything. Scopes ignore case. A key with no scopes is refused everywhere.

A key with the admin or super_admin role skips the scope check and reaches every Content API route the role reaches. It must expire within 90 days, and the Admin API refuses it.

You need an admin token in TOKEN. The quickstart shows how to get one, and creates the post content type used here.

  1. Give a role read access to post. A key reads only what its roles may read, and a key with no role reads nothing:

    Terminal window
    curl -X POST http://localhost:3001/api/admin/permissions \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"role": "site-reader", "schema_name": "post", "actions": ["read"]}'

    The answer is 200 with the rule. Access rules covers the rest of what a rule holds.

  2. Create a key with that role that may only read content:

    Terminal window
    curl -X POST http://localhost:3001/api/admin/api-keys \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"name": "website-build", "roles": ["site-reader"], "scopes": ["content:read"]}'
    {
    "id": "8c2e4f1a-3b6d-4a9e-b0c7-1d5f2e8a6b94",
    "name": "website-build",
    "roles": ["site-reader"],
    "schemas": [],
    "scopes": ["content:read"],
    "tenant_id": "default",
    "enabled": true,
    "monthly_limit": 0,
    "created_at": "2026-10-01T09:30:00Z",
    "raw_key": "ly_3f9a..."
    }

    The answer is 201. Copy raw_key into KEY and id into KEY_ID. raw_key is shown once. Only a hash is kept, so a lost key cannot be recovered.

  3. Read content with the key:

    Terminal window
    curl http://localhost:3002/api/v1/content/post \
    -H "X-API-Key: $KEY"

    The answer is the list of posts. Reading a content type no rule opens to site-reader answers 403 with permission denied.

  4. Try to write with it:

    Terminal window
    curl -X POST http://localhost:3002/api/v1/content/post \
    -H "X-API-Key: $KEY" \
    -H "Content-Type: application/json" \
    -d '{"data": {"title": "From a key"}}'
    { "error": "insufficient scope" }
  5. See what the key did:

    Terminal window
    curl "http://localhost:3001/api/admin/api-keys/$KEY_ID/audit?limit=10" \
    -H "Authorization: Bearer $TOKEN"
    [
    { "id": "f3bc4e46-...", "api_key_id": "8c2e4f1a-...", "method": "POST", "path": "/api/v1/content/post", "status": 403, "ip": "203.0.113.7", "ts": "2026-10-01T09:31:02Z" },
    { "id": "fcf64173-...", "api_key_id": "8c2e4f1a-...", "method": "GET", "path": "/api/v1/content/post", "status": 200, "ip": "203.0.113.7", "ts": "2026-10-01T09:31:00Z" }
    ]
  6. See this month's usage:

    Terminal window
    curl http://localhost:3001/api/admin/usage/api-key/$KEY_ID \
    -H "Authorization: Bearer $TOKEN"

    The answer holds billing_period, requests, bytes_in and bytes_out.

  7. Revoke the key:

    Terminal window
    curl -X POST http://localhost:3001/api/admin/api-keys/$KEY_ID/revoke \
    -H "Authorization: Bearer $TOKEN"

    The answer is 204. From then on the key answers 401 with authentication required.

A tenant holds five active keys without apikey-pro. A revoked key does not count. If a license with apikey-pro ends while a tenant holds more than five, every key keeps working, and the tenant creates no new one until it is under five again. The request history is not shortened when a license ends: the console reads the last thirty days, and the rest returns with apikey-pro.

A key can carry a monthly request limit, and past it the key answers 429. Setting a first limit or raising one needs a license with apikey-pro, while lowering or clearing one is free. See API key request limits.

Give it the editor role and the write scope, and give editor an access rule that lets it create entries of that content type. Only roles you hold can be granted, unless you are a super admin:

Terminal window
curl -X POST http://localhost:3001/api/admin/permissions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "editor", "schema_name": "post", "actions": ["read", "create"]}'
{ "name": "catalog-import", "roles": ["editor"], "scopes": ["content:read", "content:write"] }

A write is checked in this order:

MissingAnswer
The write scope403 with insufficient scope
The editor, admin or super_admin role403 with insufficient permissions
A rule that allows the action on the content type403 with permission denied

A schemas list on the key narrows it further. A key with "schemas": ["post"] answers 403 with schema not allowed for this credential on any other content type, whatever its roles allow.

Keys are managed from a signed-in session by an admin or super_admin, in the admin console or over the routes below. A request made with an API key or an admin token cannot create, revoke or delete keys.

Revoking turns a key off and keeps its history. Deleting removes the key and its history. To replace a leaked key, create a new one, move the program to it, then revoke the old one.

Key routes
MethodPathResult
GET/api/admin/api-keysYour tenant's keys, paginated with limit and offset. The secret is never shown.
POST/api/admin/api-keysCreates a key. 201. Without apikey-pro, a tenant that already holds five active keys gets 402 with "error": "cap_exceeded" and "cap": "apikey.keys". Revoke a key to free its place.
POST/api/admin/api-keys/{id}/revokeTurns the key off. 204.
DELETE/api/admin/api-keys/{id}Deletes the key and its history. 204.
GET/api/admin/api-keys/{id}/auditThe key's requests, newest first, with limit (default 50) and offset. Without apikey-pro, the last thirty days.
PATCH/api/admin/api-keys/{id}/monthly-limitSets {"monthly_limit": 50000}. 0 removes the limit. See API key request limits.
GET/api/admin/usage/api-key/{id}Requests and bytes this month.
Fields of a new key
FieldDefaultMeaning
namerequiredA label for the key.
scopes[]What the key may do.
roles[]Roles the key acts with.
schemas[]Stored and shown, not enforced.
expires_atnoneWhen the key stops working. Required within 90 days for an admin or super_admin key.
monthly_limit0Requests per calendar month in UTC. 0 means no limit. Any other value needs apikey-pro. See API key request limits.
VariableWhat it doesDefault
APIKEY_RETENTION_DAYSDays a history entry, with its client address, is kept. 0 or less keeps it forever. A value that is not a whole number uses the default.365
API_KEY_PEPPERA server secret mixed into every stored key hash, so a copy of the database alone cannot check or forge keys.unset
ENFORCE_API_KEY_PEPPERtrue stops the instance from starting unless API_KEY_PEPPER is set.false

API keys run on every install. If you set LYEVE_PLUGINS, include apikey, and usage, which counts each key's requests and enforces its monthly limit. See licensing and tiers.

StatusMessageCause
401authentication requiredThe key is unknown, revoked or expired.
403insufficient scopeThe key's scopes do not cover the route.
403schema not allowed for this credentialThe content type is outside the key's schemas list.
403permission deniedNo access rule lets the key's roles do this on the content type.
429monthly request limit (100000) exceededThe key used its monthly limit. The answer carries X-RateLimit-Exceeded: true.
Errors from the key routes
StatusMessageCause
400invalid JSON, name is requiredThe body is malformed or has no name.
402payment_requiredSetting a first monthly limit or raising one without apikey-pro.
401API keys with an admin role are not accepted on the admin API. Use an admin token.An admin-role key called the Admin API.
403insufficient permissionsThe key lacks the role a write needs.
403API keys are managed by a signed-in user, not by another keyA key called a key management route.
403This route needs a signed-in session.An admin token called a key management route.
403cannot grant roles you do not possessThe new key asks for a role you lack.
404api key not foundNo key with that id in your tenant.
422a key with the admin or super_admin role needs an expiry, at most 90 days awayAn admin-role key has no expires_at.
422a key with the admin or super_admin role expires at most 90 days awayAn admin-role key expires too late.