---
title: API keys
description: Creating, scoping, revoking and safely handling the keys that let scripts and agents act in a workspace.
section: Workspaces and access
order: 4
---

# API keys

An API key is a bearer token that lets a script, an integration or an agent work
in one of your workspaces. You create keys for **yourself** — there is no way to
mint a key that acts as somebody else.

**You need** an active membership of the workspace and a signed-in session. Any
role can create a key, viewers included.

For what to do with a key once you have one, see the
[agent and API quickstart](/docs/agents-and-api/quickstart).

## What a key is, and is not

A key **acts as its creating user, with exactly their access — no more and no
less.** Every request made with it re-runs the same membership, role and
board-access checks your own session would. There is no separate permission model
to keep in step.

That means:

- A key made by a viewer is read-only. It gets the same
  `403 Your role in this workspace is read-only.` you would.
- A key made by a member reaches the boards that member reaches, and 404s on the
  rest.
- If your role changes, your existing keys change with it, immediately. A key
  carries your _current_ access, never the access you had when you made it.
- A key is confined to the one workspace you name when you create it. It carries
  your access _within that workspace only_ — a request against a board, ticket
  or workspace it was not made for gets a `403`, even for a workspace you are
  an active member of with your own session. `GET /workspaces` through a key
  lists only that one workspace, not every workspace you belong to.
- A key cannot create a new workspace or see invitations addressed to you —
  neither names a single workspace to check the key against, so both are
  refused outright rather than scoped.

Keys work on the board and ticket API — `/workspaces`, `/boards` and `/tasks`.
They are **not** accepted on `/profile`, `/admin`, `/billing`, `/wikis`,
`/wiki-pages`, `/notifications` or `/auth`, all of which take a session token
only. In particular:

- **A key cannot mint further keys.** Key creation lives under `/profile`, which
  refuses keys, so a leaked key can never be used to manufacture more.
- **A key cannot administer the workspace.** No inviting, no role changes, no
  revoking other people's keys, no billing.

## Create a key

The secret is shown **once**, in the response to the call that creates it. Only a
hash is stored, so it cannot be shown again. If you lose it, revoke it and make a
new one.

```bash
curl -X POST https://api.laver.app/profile/api-keys \
  -H "Authorization: Bearer <your-session-token>" \
  -H "Content-Type: application/json" \
  -d '{
        "workspace_uuid": "11111111-2222-4333-8444-555555555555",
        "name": "nightly triage bot"
      }'
```

```json
{
    "api_key": {
        "uuid": "66666666-7777-4888-8999-000000000000",
        "workspace_uuid": "11111111-2222-4333-8444-555555555555",
        "name": "nightly triage bot",
        "token_hint": "laver_Ab3xQ9",
        "last_used_at": null,
        "created_at": "2026-01-01T09:00:00.000Z"
    },
    "token": "laver_..."
}
```

Copy `token` straight into your secret store. `token_hint` is the first few
characters, kept so you can tell your keys apart in a list — it is far too little
to use.

Give the key a name that says what it is for. When something goes wrong, the name
is what tells you which key to revoke.

## List your keys

```bash
curl https://api.laver.app/profile/api-keys \
  -H "Authorization: Bearer <your-session-token>"
```

Returns your live keys across every workspace you are still an active member of,
newest first, each with its workspace, name, hint and `last_used_at`. Secrets are
never returned. `last_used_at` is what makes a key safe to revoke: a key that has
never been used, or has not been used in months, is one you can retire without
guessing.

## Delete a key

```bash
curl -X DELETE https://api.laver.app/profile/api-keys/66666666-7777-4888-8999-000000000000 \
  -H "Authorization: Bearer <your-session-token>"
```

Revocation is immediate — the very next request made with that key answers
`401 {"error": "That API key is not valid or has been revoked."}`. It cannot be
undone, and it cannot be reversed by recreating a key with the same name.

You can only delete your own keys this way. An owner or admin can revoke **any**
key in their workspace from **Admin → API keys**, which also lists every key with
the person it belongs to.

## Ten keys per workspace

You may hold **ten live keys in a single workspace**. The eleventh is refused:

```json
{
    "error": "You already have 10 keys in this workspace. Revoke one first."
}
```

The status is 409. Revoked keys do not count, so retiring one frees the slot
immediately. The cap is per workspace, so belonging to three workspaces means up
to ten keys in each.

## What stops a key working

| Event                                                      | Effect on the key                                    |
| ---------------------------------------------------------- | ---------------------------------------------------- |
| You delete the key                                         | Dead immediately                                     |
| An owner or admin revokes it                               | Dead immediately                                     |
| **The person it belongs to is deactivated**                | **All of their keys in that workspace stop working** |
| The person it belongs to is removed from the workspace     | Same — all of their keys there stop working          |
| The workspace is deleted                                   | Memberships are deactivated, so the keys stop        |
| That person's role is changed                              | Key keeps working, with the new role's access        |
| That person changes their password or signs out everywhere | **Key keeps working**                                |

The deactivation rule is the important one: switching someone off in **Admin →
People** stops their agents with them, in one action, without hunting for keys.
It is the offboarding step that matters most.

The last row is the counterpart, and it surprises people: "log out everywhere"
ends _sessions_. A key is not a session, so it survives. Revoke keys explicitly.

## Keep the secret secret

A key is a password that does not expire and belongs to a person. Treat it like
one.

**Never** put a key into:

- a support ticket, a bug report or an issue description
- a comment on a ticket, or a wiki page
- a log line, a stack trace, an error report or an analytics event
- a screenshot, a screen recording or a shared terminal session
- a commit, a config file in version control, or a CI job's plain-text variables
- a chat message, "just for a second"

Instead: put it in a secret manager or an environment variable, pass it only in
the `Authorization` header, and redact anything beginning `laver_` before you
paste output anywhere. Support will never ask you for a key — if anyone does, that
is the whole of the reason not to send it.

Note that requests made with a key share a rate-limit budget by source address
rather than getting their own per-user budget, so several noisy agents behind one
address can throttle each other.

## If a key leaks

Assume it is being used. Do this in order:

1. **Revoke it now** — `DELETE /profile/api-keys/<api-key-uuid>`, or **Admin → API
   keys** if it belongs to someone else or they are unavailable. Revoking is
   instant and cheap; there is nothing to weigh up.
2. **Issue a replacement** and deploy it to whatever was using the old key. Do this
   after revoking, not before — a leaked key that is still live while you tidy up
   is the risk.
3. **Check what it could reach.** A key holds its owner's whole access: their
   role and their boards in **every** workspace they are a member of, not only
   the one the key names. That is the blast radius — work it out from the
   owner's memberships, not from the key.
4. **Check the audit log** (**Admin → Audit log**) around the time of the leak.
   Key creation and revocation are both recorded, so an unfamiliar
   `api_key.created` entry is worth investigating.
5. **If you cannot reach the key's owner,** deactivate them in **Admin → People**.
   That stops every key they hold in the workspace at once.
6. **Scrub the place it leaked** — edit the comment, rotate the log, delete the
   screenshot. Do this last: it never substitutes for revoking, because you cannot
   know who already read it.
