> Source: https://wexa.ai/docs/administration/credential-management

# Credential management

A program calling Wexa authenticates with an **API key** scoped to one project. This page covers
issuing one, seeing what exists, replacing one, revoking one, and — the part most worth getting
right — who should hold one at all.

## Issuing a key

The screen is the API keys page, which lists the keys for whichever project you are working in and
offers **Create API key**. The endpoint behind it is `POST /v1/apikeys`.

Four things are true of every issue, and each will bite if you are surprised by it.

**It takes a user's sign-in token, not another API key.** A key cannot mint a key. The call must
carry a Wexa user token.

**You must be an administrator on the target project.** Anyone else gets
`403 "API key creation requires an admin role on this project"`. If the token does not carry a role
itself, the gateway looks up the caller's real role on that project rather than believing the client.

**A name is required.** An empty name is refused with `400 name is required`. This is not a screen
nicety — the key's identifier is a random string with no meaning in it, so a nameless key is one you
will not be able to tell apart from its neighbours in six months.

**The secret is shown exactly once.** The response carries it along with the note *store this secret
now; it is not retrievable again*, and the screen reveals and copies it in the dialog that follows
creation. Nothing can show it to you afterwards — not the list, not support, not the database.
Losing it means issuing a new key.

The request takes the organization, department and project the key is for, its name, and optionally
an explicit list of permissions.

### What a key can do

If you request no permissions explicitly, the key gets the default set for the role of the person
minting it:

- **An administrator's key covers the whole tool surface** — reading the context graph and the
  documentation, editing the ontology, reading and writing orchestration, running an agent, creating
  skills, and changing which model the organization runs on.
- **Any other role, and any role the gateway does not recognise, falls closed** to reading the
  context graph and reading the documentation. Nothing else.

An explicitly requested list always wins over the default, so a key asked for narrowly stays narrow
however senior its minter is. The design principle is simple and worth stating to anyone who asks
for a key: **a key can do nothing the person who minted it could not already do**.

Two permissions are not in any role's default set because they gate REST planes rather than tools:
writing to the data catalog, and pushing local code changes into the code graph. Ask for those
explicitly when a program needs them.

## Listing keys

`GET /v1/apikeys?project_id=…`, and the screen's table. The columns are the name, the key identifier,
who created it, when, when it was last used, and its status.

Secrets are never returned by the list. They were only ever shown once, at creation.

Listing requires an administrator on that project — the same check as issuing, with the message
`403 "requires an admin role on this project"`.

## Revoking a key

`DELETE /v1/apikeys/{id}?project_id=…`, and the row's revoke action in the screen.

The project is required and is checked against the key's own scope before anything happens, so an
administrator of one project cannot revoke another project's key by guessing its identifier — that
returns `404 "no such API key in this project"`.

Both issuing and revoking write an entry to the audit trail recording who did it, their role, the key
and the project. Read [lifecycle and audit](/docs/concepts/lifecycle-and-audit) before you rely on
that record for anything: it says plainly how long those entries survive.

## Rotating a key

1. **Issue a new key** for the same project, naming it so you can tell the two apart — a date in the
   name is enough.
2. **Move the callers over** and confirm the new key's *last used* column is moving in the list.
3. **Revoke the old key.** Revoking before step 2 is what turns a rotation into an outage.

Because there is no rotate action, there is also no automatic expiry to lean on: a key stays valid
until somebody revokes it. Put a rotation in your own calendar rather than waiting for the product to
ask.

## Who should hold a key

A short set of rules that follow from the above:

- **One key per program, never one per team.** Keys are told apart by name and revoked
  individually, so a key shared by four programs cannot be withdrawn from one of them.
- **Name the key after the program that holds it**, not after the person who created it. The creator
  is already recorded in the list; the holder is not recoverable from anywhere else.
- **Mint the key from the narrowest account that can do the job.** The default permissions follow the
  minter's role, so an administrator minting a key for a read-only reporting job should request the
  read permissions explicitly rather than accept the administrator default.
- **A person does not need a key.** People sign in; keys are for programs. If someone asks for one to
  use by hand, what they want is a project login.
- **Revoke on departure, explicitly.** Do not assume that suspending someone's account withdrew the
  keys they issued — issue and revocation are separate acts, and the keys they minted are recorded
  against the project, not against their continued employment.

## Rate limits on a key

One ceiling applies per key rather than per project: the OpenAI-compatible chat-completions endpoint
allows 60 requests a minute per key, in fixed one-minute windows. It applies to that endpoint only
and ceilings nothing else the key can do.

The project and organization ceilings apply on top of it and are shared by every caller, key or
person. See [quota and credits](/docs/concepts/quota-and-credits).

## Using a key

Once issued, the key is what the four surfaces authenticate with — the Wexa MCP server, the REST
API, and both SDKs. The surface pages cover the header, the environment variable and the client
constructor for each.
