# API key

Every request to Kestrel carries an API key. Keys belong to a workspace, can be limited to some repos, and can be rotated without downtime.

## Create a key

Open **Settings → API keys** in the dashboard and choose **Create key**. Name it after where it will live, like `ci` or `laptop`, so you know which one to revoke later.

Keys start with `kst_live_`. Test keys start with `kst_test_`: they run in sandboxes that never open pull requests, which is handy in CI for the SDK itself.

## Use it

The SDKs and the CLI read `KESTREL_API_KEY` from the environment. Pass it yourself when you need more than one workspace in a process.

```ts TypeScript
const kestrel = new Kestrel({ apiKey: process.env.KESTREL_CI_KEY });
```

```python Python
kestrel = Kestrel(api_key=os.environ["KESTREL_CI_KEY"])
```

For the REST API, send it as a bearer token:

```bash Terminal
curl https://api.kestrel.dev/v1/runs \
  -H "Authorization: Bearer $KESTREL_API_KEY"
```

## Limit a key to some repos

A key can see every repo in the workspace, or only the ones you pick. A key limited to `acme/web-app` gets a `403` for any other repo, so a leaked CI key cannot start runs elsewhere.

| Scope | Can do |
|---|---|
| `runs:write` | Start, cancel and retry runs |
| `runs:read` | Read runs, steps and traces |
| `sandboxes:write` | Create, pause and kill sandboxes |
| `memory:write` | Edit and clear repo memory |

## Rotate a key

Create the new key, deploy it, then revoke the old one. Both work in the meantime, so nothing fails while the change rolls out.

> [!WARNING]
> Revoking a key stops runs it started at their next step. Runs already past the eval gate still open their pull requests.
