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

# vetta keys

> Create, list, rotate, and revoke scoped API keys.

API keys are how CI and services authenticate. Each key carries an explicit scope list, so a deploy key need not be an admin key.

## Commands

| Command                      | Description                                  |
| ---------------------------- | -------------------------------------------- |
| `vetta keys create`          | Mint a scoped key. The secret is shown once. |
| `vetta keys list`            | List keys — metadata only, never secrets.    |
| `vetta keys rotate <key-id>` | Mint a replacement secret for a key.         |
| `vetta keys revoke <key-id>` | Revoke a key immediately.                    |

## create

```bash theme={"system"}
vetta keys create --name ci --scopes agents:write,sessions:write,billing:read
```

| Flag       | Description                            |
| ---------- | -------------------------------------- |
| `--name`   | Human-readable label (required).       |
| `--scopes` | Comma-separated scope list (required). |

`--scopes` is validated client-side against the shared scope enum, so an unknown scope fails before the request is sent.

<Warning>
  The `secret` in the `create` and `rotate` replies exists nowhere else. It is not stored in retrievable form and no route returns it again. Capture it now or mint another key.
</Warning>

## list

```bash theme={"system"}
vetta keys list --limit 2
```

```json theme={"system"}
{
  "data": [
    {
      "id": "key_gwpjrs1egky00g6vw5gjczj6fs",
      "object": "api_key",
      "name": "ci",
      "prefix": "sk_test_pdme",
      "scopes": ["agents:write", "agents:read", "sessions:write", "billing:read"],
      "created_by": { "type": "key", "id": "key_66nnedy98yfx3ygzssfv99p7tf" },
      "last_used_at": null,
      "created_at": "2026-08-22T23:56:53.464Z"
    }
  ],
  "has_more": true,
  "next_cursor": "key_m3jjzgc4zsffsnqhvwwf28m9d1"
}
```

`prefix` is the 12-character display prefix — the same value `vetta whoami` reports as `key_prefix`, and the only part of a secret that is ever readable. `last_used_at` is `null` for a key that has never authenticated a request, which makes unused keys easy to find:

```bash theme={"system"}
vetta keys list --limit 100 | jq -r '.data[] | select(.last_used_at == null) | .id'
```

| Flag      | Description                                  |
| --------- | -------------------------------------------- |
| `--limit` | Page size.                                   |
| `--after` | Cursor from a previous page's `next_cursor`. |

## rotate & revoke

```bash theme={"system"}
vetta keys rotate key_gwpjrs1egky00g6vw5gjczj6fs
vetta keys revoke key_gwpjrs1egky00g6vw5gjczj6fs
```

Neither takes a flag; both take the key id as a positional argument.

`rotate` returns a new secret, once. `revoke` takes effect immediately — in-flight requests holding the old secret start failing with `401`. Both actions are recorded in the [audit log](/docs/cli/audit) as `api_key.rotated` and `api_key.revoked`.

<Note>
  Test-mode keys (`sk_test_…`) run against the no-real-money seam: sessions execute and the ledger moves, but no charge reaches a payment processor. Check `mode` in [`vetta whoami`](/docs/cli/auth#whoami) if you are unsure which you hold.
</Note>
