# API keys

Source: https://docs.lookover.ai/webagent/concepts/api-keys

> Create an API key, set an expiry, and use a CI key with least privilege for pipelines.

An API key lets a script, an MCP client or a pipeline call webagent as your project. The key decides the organization, the project and the role of every call. Keys look like `wa_us_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`.

Create a key in the dashboard: **API keys** in the sidebar, then **Create key**. The full key is shown once, so save it at once (a CI secret, a password manager). The list afterwards shows only the start of the key. An admin can also create keys with `POST /v1/keys` and revoke one with `DELETE /v1/keys/:keyId`.

| Option | Values | Default |
|---|---|---|
| `name` | A label, so you can tell keys apart | Required |
| `role` | `member` or `admin` | `member` |
| `expiresAt` | An ISO date and time in the future | Never |
| `scope` | `full` or `ci` | `full` |

Make one key per job or machine, so you can revoke it alone.

## Expiry [#expiry]

Give a key an expiry (`expiresAt`) when it is for a short job or a contractor. The dashboard form offers **Never**, **30 days**, **90 days** or a date you pick. It sends one `expiresAt`. The API refuses a time in the past, or one too far ahead, and the error message says the limit.

After that time the key stops working. A call with it fails with `401` and code `unauthorized`:

```json title="An expired key"
{ "error": { "code": "unauthorized", "message": "API key expired on 2026-11-13T00:00:00.000Z; create a new key" } }
```

Create a new key and replace the secret in your CI. The keys list shows **Expires** with the date, **Expired**, or **Never**. A key made before expiry existed never expires, and keeps its full scope.

## Scope: full or CI [#scope-full-or-ci]

A `full` key can do whatever its role allows. A `ci` key is for pipelines. It can run tests and read results, and nothing else. If a pipeline key leaks, it cannot change your team, billing or settings. In the list, a `ci` key has a **CI** badge.

Choose **CI** in the create form, or send `"scope": "ci"`:

```bash title="A CI key that expires in a few months"
curl -s -X POST $API_URL/v1/keys \
  -H "Authorization: Bearer $WEBAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"github-actions","scope":"ci","expiresAt":"2027-01-31T00:00:00Z"}'
```

(Use an admin key for this call. Set `API_URL` as in the [Quickstart](/quickstart).)

### What a CI key can do [#what-a-ci-key-can-do]

| Area | Allowed |
|---|---|
| Sessions | Create, list, read, update and stop sessions. Get a session token. Read the recording and the logs. Create and remove a share link |
| Tasks | List tasks, and read a task report (`GET /v1/tasks`, `GET /v1/sessions/:id/tasks`, `GET /v1/tasks/:id/report.json`) |
| Skills | List and read skills, run a skill, list skill runs and read their reports |
| Workflows | List and read workflows. Save a workflow by key (`PUT /v1/workflows/by-key/:key`, what `webagent test --sync` does). Start a run (`POST /v1/workflow-runs`, `POST /v1/workflows/:id/runs`), list and read runs, read `report.md`, cancel a run |
| MCP | The hosted MCP endpoint, `POST /mcp` |

Everything else is refused, including API keys, team and organization, billing, settings, credentials, the audit log, projects, and creating, editing or deleting a workflow in the dashboard. A new route stays closed to CI keys until it is opened on purpose.

A refused call fails with `403` and code `forbidden`: "This is a CI key; use a full key or the dashboard for this". `error.details.reason` is `ci_scope`. A run that a CI key started before its expiry finishes normally.

Use a CI key with the [GitHub Action](/guides/qa-in-ci#1-set-up) or the [Smoke tests in CI](/guides/ci) script.
