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

# API Keys

> Legacy and scoped API keys — creating, rotating, and revoking access

## Overview

Every request to the Lua API — from the CLI, a device client, or a direct HTTP integration — authenticates with an API key. There are two kinds of key:

<CardGroup cols={2}>
  <Card title="Legacy keys" icon="key">
    `api_` followed by 32 hex characters. Created before scoped keys existed. Act with the owner's full permissions in every organization the owner belongs to.
  </Card>

  <Card title="Scoped keys" icon="shield-halved">
    `api_<uuid>.<secret>`. Personal keys with an explicit role on one or more organizations or agents — never more than the owner could do themselves.
  </Card>
</CardGroup>

Both kinds are used exactly the same way at request time — see [Using a key](#using-a-key). The difference is entirely in how much access the key carries.

## Legacy keys

If your key was created before scoped keys shipped, nothing about it changes. Legacy keys:

* Act with the owner's full permissions, in **every** organization the owner belongs to.
* Keep working indefinitely.

<Note>
  There is no deprecation date, no forced rotation, and no expiry for legacy keys. You are not required to switch to a scoped key — do it when it's convenient, e.g. the next time you set up a new integration.
</Note>

## Scoped keys

A scoped key is a **personal** key: it belongs to one member, and it carries an explicit role on each organization or agent it's granted against — the same kind of role a human member holds on that resource in the admin dashboard.

Two rules govern what a scoped key can do:

<Steps>
  <Step title="Capped at the owner's role">
    A scoped key can never do more than its owner can. Its effective role on a resource is capped at the owner's current role there, even if the key was originally granted a broader role.
  </Step>

  <Step title="Follows the owner's access">
    If the owner loses access to an organization, any key of theirs scoped to that organization loses access too. Keys the owner holds on their other organizations keep working normally.
  </Step>
</Steps>

<Note>
  The role picker in the dashboard (**Settings → API Keys**) shows the current set of roles you can grant, since it's the same roster used for human org members. Scope each key to the organizations or agents it actually needs — see [Best practices](#best-practices).
</Note>

## Creating a key

Keys are created in the admin dashboard, under **Settings → API Keys**.

```bash theme={null}
lua admin
# Or visit https://admin.heylua.ai
```

<Steps>
  <Step title="Open Settings → API Keys">
    This is a personal section — you're managing your own keys, or (if you're an org admin) keys on behalf of another member.
  </Step>

  <Step title="Choose the scope">
    Grant a role on one or more organizations or agents. An org admin creating a key for another member has the key capped at that member's role — it can't exceed what the key's owner can already do.
  </Step>

  <Step title="Set an optional expiry">
    Defaults to never. Set a date if the key is for a temporary integration.
  </Step>

  <Step title="Copy the secret">
    The full secret is shown exactly once, at creation. Store it in a secrets manager or `.env` file — it can't be viewed again after you navigate away.
  </Step>
</Steps>

<Warning>
  Never commit an API key to version control or share it publicly.
</Warning>

## Managing a key

<AccordionGroup>
  <Accordion title="Rotate">
    Replaces the key's secret while keeping its role and grants intact. Only the key's owner can rotate it. The new secret is shown exactly once, the same as at creation — the old secret stops working immediately.
  </Accordion>

  <Accordion title="Suspend / reactivate">
    Temporarily disables a key without losing its configuration. A suspended key fails authentication until it's reactivated; its role and grants are untouched in the meantime.
  </Accordion>

  <Accordion title="Revoke">
    Permanently disables a key. Either the key's owner or an org admin can revoke it. Revocation is one-way — a revoked key can't be reactivated, only replaced with a new one. It takes effect within about a minute everywhere.
  </Accordion>
</AccordionGroup>

## Using a key

Usage is identical for legacy and scoped keys:

<Tabs>
  <Tab title="HTTP API">
    ```bash theme={null}
    Authorization: Bearer YOUR_API_KEY
    ```

    See the [HTTP API reference](/channels/http-api).
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    export LUA_API_KEY=your-api-key
    # or
    lua auth configure --api-key your-api-key
    ```

    See [Authentication](/cli/authentication).
  </Tab>

  <Tab title="Device clients">
    ```typescript theme={null}
    new DeviceClient({
      apiKey: process.env.LUA_API_KEY!,
      agentId: 'your-agent-id',
      // ...
    });
    ```

    See the [Devices quickstart](/devices/quickstart).
  </Tab>
</Tabs>

## Errors

| Status | Meaning                                                                    |
| ------ | -------------------------------------------------------------------------- |
| `401`  | The key is invalid, expired, suspended, or revoked.                        |
| `403`  | The key is valid, but its role doesn't allow that action on that resource. |

A `403` on a scoped key usually means the key needs a broader role, or a grant on an additional organization or agent — check **Settings → API Keys** in the dashboard.

## Best practices

<AccordionGroup>
  <Accordion title="One key per integration">
    Don't reuse a single key across unrelated integrations. If one is compromised or needs rotating, a dedicated key limits the blast radius and makes it obvious what to revoke.
  </Accordion>

  <Accordion title="Grant the narrowest role that works">
    Give a key only the role it needs on a resource, not a broader one "to be safe." A key capped at the right role can't be misused for actions it was never meant to perform.
  </Accordion>

  <Accordion title="Prefer agent-level over org-level scope">
    If an integration only ever needs one agent, scope the key to that agent instead of the whole organization. It keeps the key from reaching agents it has no business touching.
  </Accordion>

  <Accordion title="Set an expiry for temporary access">
    Contractor access, a one-off migration script, a demo environment — anything with a known end date should get an expiry instead of relying on someone remembering to revoke it later.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="CLI Authentication" icon="terminal" href="/cli/authentication">
    Configure the CLI with an API key
  </Card>

  <Card title="HTTP API" icon="bolt" href="/channels/http-api">
    Call your agent directly over HTTP
  </Card>
</CardGroup>
