> ## Documentation Index
> Fetch the complete documentation index at: https://docs.assetinfinity.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# API keys

> Issuing a credential a script, an integration or an AI agent can use to reach this organisation's data, on its own account rather than a person's.

A credential a machine carries. Where a person signs in with a password, a script, an integration or
an AI agent authenticates with a key — and this is where one is issued, rotated and revoked.

## Two lists, in that order

**Identities** first, **keys** second — because a key can never do more than the identity it acts
as, so reading a key's scopes without having read the identity behind it is reading half the answer.

| List | What it holds |
| - | - |
| **What a key can act as** | The machine identities (service accounts) this organisation has created — each with its own roles, site access, and whether it may act for a named person rather than only for itself |
| **The keys** | The credentials themselves — one row per key, each pointing at the identity it carries |

<Note>
  An identity is not only for a traditional integration. The same mechanism is what a connected AI
  assistant runs on: the [MCP server](/config/mcp) carries an identity and a key exactly like any
  other machine caller, and appears in both lists here like one.
</Note>

## Creating an identity

**New identity** asks for a code (its permanent address — lower case letters, digits and hyphens),
a name, and what it's for. It starts with no roles, acting only as itself, and reaching every site.

Nothing here grants it anything. Give it the roles the integration needs on
[Access](/setup/access), narrow it to a site there if it should only reach one, and come back here
to issue it a key.

### Acting as itself, or as a person

**Acts as** toggles between two things a key on this identity can do:

| Setting | Meaning |
| - | - |
| **Itself** | The default. Every question the key asks is answered against the identity's own roles |
| **A person** | The key may exchange for a token naming whoever it is acting for, so the answer is narrowed to *that person's* permissions too |

This is off by default on purpose: an ordinary integration key must not be able to become anybody.
Turning it on is a sentence an administrator reads, not a property a key picks up by being used a
certain way — and it is what an integration serving several different people needs, the MCP server
being the case this product ships with.

## Issuing a key

**Issue a key** asks which identity it acts as, a name, how long it lasts, and what to narrow it to.

| Field | Notes |
| - | - |
| **Acts as** | The identity whose roles are the ceiling — nothing below this can raise it |
| **Stops working after** | Between 1 and 365 days. There is no option for a key that never expires |
| **Narrow it to** | Permissions the *identity* holds, grouped by module, with an "all of it" toggle per module. Choosing nothing gives the key everything the identity can do |

<Warning>
  **The key is shown once.** Nothing in this product can read it back afterwards — not an
  administrator, not support. Copy it before closing the dialog; a lost key is replaced, not
  recovered.
</Warning>

## Rotating and revoking

**Rotate** issues a successor with the same name, identity and scopes, and gives the key it replaces
a grace window — up to 90 days — so whatever uses it can be moved over before it stops. The window
only ever brings the old key's expiry in, never past the date it already had.

**Revoke** stops a key being traded for a token at once, and needs a reason — somebody will ask,
months later, when an integration stops working and nobody remembers whether it was deliberate.
There is no undo; a replacement is a new key.

**Suspend**, on an identity, stops every key it holds at once — including ones nobody remembers
issuing. It's the bigger action, which is why it sits on the identity rather than on a key.

## Using a key

A key is never sent on an ordinary request. It's traded once for a session token, and the token —
good for fifteen minutes — goes on every request after that, the same way a browser's session does:
the same roles, the same site limits, the same audit trail.

```bash theme={null}
curl -X POST "$CMMS_URL/api/rpc/exchange_api_key" \
  -H 'content-type: application/json' \
  -d '{"p_key": "cmk_…"}'

# → { "token": "...", "expires_at": "...", "scopes": [...] }
curl "$CMMS_URL/api/work_orders" -H "authorization: Bearer $TOKEN"
```

The token is meant to be reused for its whole fifteen minutes — asking for a new one on every
request is refused as a rate limit. Revoking a key stops it being traded again immediately, and
stops any token already traded from writing anything; a plain read can still finish out the token's
last few minutes.

## Who can manage this

| Permission | Covers | Default |
| - | - | - |
| **View** | See which keys and identities exist, their scopes and last use | System Administrator |
| **Issue and rotate** | Create keys, including rotation | System Administrator |
| **Revoke** | Revoke a key | System Administrator |
| **Administer identities** | Create and suspend service accounts | System Administrator |

Unlike [workflows](/config/workflows) and [rules](/config/rules), none of this is on the
Maintenance Administrator's default pattern — a credential that can reach the whole API is not
maintenance configuration. See [roles](/setup/roles).

<Card title="MCP" icon="plug" href="/config/mcp">
  How an AI assistant connects to this data using exactly this kind of credential.
</Card>
