---
title: API keys
description: Create, scope, rotate and revoke keys for scripts and integrations.
sidebar:
  label: API keys
---

{/* Source of truth: packages/shared/src/api-keys.ts — ApiKeyStatuses,
    ApiKeyExpiryDays and the capability registry, both RENDERED by
    components/ApiKeyExpiry.astro and components/ApiKeyCapabilities.astro
    rather than transcribed. Token format: gl_ prefix,
    packages/core/src/crypto/token.utils.ts (salted SHA-256 at rest, 11-char
    display prefix). Scoping: API_KEY_ASSIGNABLE_PERMISSIONS in
    permissions.ts. Limit: maxApiKeys in plans.ts. */}

An API key lets a script, a CI job or an AI agent act on your workspace without
a browser session.

```http
Authorization: Bearer gl_...
```

## Creating one

Keys are created in **Settings → API keys**, with a name, an expiry, and a set
of permissions.

<ApiKeyExpiry />

:::caution
The key is shown **once**, at creation. Glowo stores only a salted hash and can
never show it again — if you lose it, rotate the key and update whatever used
it. Only the first 11 characters are kept for display, so you can tell keys
apart in the list.
:::

## Where a key may be used

A key also declares its **surfaces** — which APIs will accept it at all.

<ApiKeySurfaces />

A key minted for `mcp` is refused on the REST API with `403`, and a `rest` key
is refused by the agent transport. The credential is perfectly valid in both
cases; it is simply not for that door, and re-presenting it will never help.

This matters more than it looks. The MCP tool list is a much smaller surface
than the REST API — there is no status-page-delete tool, but there is a REST
route — so a key someone reviewed as "it can only use these tools" must not also
open everything behind the REST guard.

:::caution
Surfaces are the one field here whose default is the **wide** one. Omitting it
grants **both**, which is the behaviour that existed before surfaces did.
Capabilities and expiry both narrow by omission; this one does not.
:::

## Capabilities a key can hold

<ApiKeyCapabilities />

The four marked unassignable are session-only by design. A leaked key must not
be able to change your billing, add members, alter workspace settings, or read
the audit log that would show what it did.

Give each key the least it needs. A key that only reads monitors for a dashboard
should carry `monitors:read` and nothing else — not `monitors:write`, and
certainly not `monitors:delete`.

## Rotating and revoking

- **Rotate** issues a new secret for the same key, keeping its name and
  permissions. Use it on a schedule and whenever someone with access leaves.
- **Revoke** disables the key immediately while keeping the record, so the audit
  trail survives.
- **Delete** removes it entirely.

Rotate rather than delete-and-recreate: rotation keeps the key's identity and
history intact.

## Keeping keys safe

- **Never commit a key.** Use your secret manager or CI variables.
- **Never put a key in client-side code.** For a public status widget use a
  [publishable embed key](/status-pages/embeds-and-badges) instead — that is
  what it is for.
- **One key per consumer.** Separate keys for CI, your dashboard and your AI
  agent mean you can revoke one without breaking the others.
- **Review them periodically.** Key creation, rotation and revocation are all
  recorded in the [audit log](/organization/audit-log).

:::note
Keys belong to the workspace, not to the person who created them. Removing a
member does not revoke keys they made — review keys as part of offboarding.
:::
