# API keys

An API key authenticates every call your team makes to the Impello API.
Calls to a sandbox itself carry a sandbox-scoped access token that the SDK
attaches for you (see [Network](/docs/sandbox/network) and
[Filesystem](/docs/sandbox/filesystem)).

## Where to find your key

Keys are created and read at
[dashboard.impello.ai/keys](https://dashboard.impello.ai/keys). That page is
the only place a key is ever shown.

- A key belongs to the team, not to the person who made it. Every member of
  the team can see and manage the team's keys.
- A team can hold several keys. Give each one a name of up to 64 characters,
  and rename it later if you want.
- The full key is shown once, when it is created. Impello stores a hash of it,
  so a key you did not copy cannot be recovered, only replaced.
- The keys page lists every key the team created, with its name, a masked
  value, when it was created and when it was last used. The reserved
  `dashboard-session` key that keeps your browser signed in is hidden, and
  cannot be renamed or deleted there.
- A key carries no expiry date: the keys page records only when it was
  created and when it was last used.

## Set it once

Export the key as `IMPELLO_API_KEY` and both SDKs pick it up. Nothing else on
this page is required.

```bash
export IMPELLO_API_KEY=imp_...
```

<CodeTabs>
<Tab label="TypeScript">

```ts
import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base')
const result = await sandbox.commands.run('echo hello')
console.log(result.stdout)

await sandbox.kill()
```

</Tab>
<Tab label="Python">

```python
from impello import Sandbox

sandbox = Sandbox.create("base")
result = sandbox.commands.run("echo hello")
print(result.stdout)

sandbox.kill()
```

</Tab>
<Tab label="curl">

```bash
curl "https://api.sandbox.impello.ai/v2/sandboxes" \
  -H "X-API-KEY: $IMPELLO_API_KEY"
```

</Tab>
</CodeTabs>

Over HTTP the key travels in the `X-API-KEY` header. The base URL is
`https://api.sandbox.impello.ai`.

## Pass it per call

Every call that reaches the API also accepts the key as an option. A key
passed this way wins over the environment variable.

<CodeTabs>
<Tab label="TypeScript">

```ts
import { Sandbox } from '@impello/sdk'

const sandbox = await Sandbox.create('base', {
  apiKey: process.env.TEAM_A_KEY,
})

const paginator = Sandbox.list({ apiKey: process.env.TEAM_B_KEY })
const sandboxes = await paginator.nextItems()
```

</Tab>
<Tab label="Python">

```python
import os

from impello import Sandbox

sandbox = Sandbox.create("base", api_key=os.environ["TEAM_A_KEY"])

paginator = Sandbox.list(api_key=os.environ["TEAM_B_KEY"])
sandboxes = paginator.next_items()
```

</Tab>
</CodeTabs>

In Python you can bind the key to a client once and use `client.Sandbox` and
`client.AsyncSandbox` in place of the top-level classes. A parameter passed to
a single call beats the client, and the client beats the environment. The
TypeScript SDK has no client class; pass `apiKey` per call instead.

```python
import os

from impello import Impello

client = Impello(api_key=os.environ["TEAM_A_KEY"])
sandbox = client.Sandbox.create("base")
```

## What a key can do

A key acts for the whole team. It can create, connect to, list, pause, resume
and kill the team's sandboxes, read their logs and metrics, change their
network rules, and build, list and delete the team's templates. It cannot
read, create, rename or delete API keys; the API refuses that route to a team
key, and those operations live in the dashboard, behind your login.

<Callout kind="warning">
Keep the key on your server. Anyone holding it can act as your team. Do not
ship it to a browser or a mobile app, and do not commit it to a repository.
When a browser needs to reach a sandbox, hand it a signed URL
([Filesystem](/docs/sandbox/filesystem)) or the sandbox's traffic access token
([Network](/docs/sandbox/network)) instead of the API key.
</Callout>

## Revoke and rotate

Delete a key on the keys page to revoke it. The deletion cannot be undone, and
anything still using that key is refused.

To rotate a key without downtime:

1. Create a new key on the keys page.
2. Set `IMPELLO_API_KEY` to the new value everywhere the old one is used, and
   redeploy.
3. Delete the old key. Its "last used" column tells you whether anything is
   still on it.

## Key format

A key is `imp_` followed by hexadecimal characters. Both SDKs check the shape
before sending a request, and the two checks are not the same. The Python SDK
requires `imp_` plus hex and raises `AuthenticationException` on anything else.
The TypeScript SDK is looser: it also accepts the older prefix, and raises
`AuthenticationError` only when the key is not shaped like a key at all.
Neither check decides whether the key is a valid one; that is the API's answer.

A missing key raises the same error type, with a message pointing at the API
Keys tab on the dashboard. The TypeScript message still names the older
environment variable and shows an old-prefix example key — set
`IMPELLO_API_KEY` to an `imp_` key. A key the API rejects comes back as HTTP
401, which the SDKs raise as `AuthenticationError` or `AuthenticationException`
with the message `Unauthorized, please check your credentials.` — Python
prefixes it with `401: `. See [Errors](/docs/errors).

The TypeScript SDK can skip the shape check: pass `validateApiKey: false` or
set `IMPELLO_VALIDATE_API_KEY=false`. The Python SDK always checks; its
`validate_api_key` parameter is deprecated and has no effect.

A key minted before Impello moved to its own prefix cannot be made to work by
any client, and the two SDKs say so at different moments. Python refuses it
locally, raising `AuthenticationException` with a message naming the 2026-08-30
cutover. TypeScript lets it through the shape check, so the request goes out
and comes back as HTTP 401. Replace the key on the keys page; see
[Migrate](/docs/migrate-from-e2b).

## Other settings

Each setting is read from the environment under its `IMPELLO_` name. The older
prefix is still read as a fallback, so an existing environment keeps working —
see [Migrate](/docs/migrate-from-e2b).

<div className="docs-table-wrap">
<table>
  <thead>
    <tr><th>Variable</th><th>Default</th><th>What it does</th></tr>
  </thead>
  <tbody>
    <tr><td>`IMPELLO_API_KEY`</td><td>none</td><td>The key. Starts with `imp_`</td></tr>
    <tr><td>`IMPELLO_DOMAIN`</td><td>`sandbox.impello.ai`</td><td>The domain the API and the sandboxes sit on</td></tr>
    <tr><td>`IMPELLO_API_URL`</td><td>`https://api.` plus the domain</td><td>The API address</td></tr>
    <tr><td>`IMPELLO_VALIDATE_API_KEY`</td><td>`true`</td><td>Set to `false` to skip the key shape check. TypeScript only</td></tr>
    <tr><td>`IMPELLO_DEBUG`</td><td>`false`</td><td>Talk to a local API at `http://localhost:3000` and reach sandboxes on `localhost`, for running the SDK against a local stack</td></tr>
  </tbody>
</table>
</div>

Most of these settings, and a few that have no environment variable, can be
passed on any call that reaches the API: `Sandbox.create`, `Sandbox.connect`,
`Sandbox.list`, `Sandbox.kill` and the rest. Where a TypeScript option is
narrower than that, the table says so.

<div className="docs-table-wrap">
<table>
  <thead>
    <tr><th>TypeScript</th><th>Python</th><th>Default</th><th>What it does</th></tr>
  </thead>
  <tbody>
    <tr><td>`apiKey`</td><td>`api_key`</td><td>`IMPELLO_API_KEY`</td><td>The key</td></tr>
    <tr><td>`domain`</td><td>`domain`</td><td>`IMPELLO_DOMAIN`</td><td>The domain</td></tr>
    <tr><td>`apiUrl`</td><td>`api_url`</td><td>`IMPELLO_API_URL`</td><td>The API address</td></tr>
    <tr><td>`requestTimeoutMs`</td><td>`request_timeout`</td><td>`60000` ms / `60.0` s</td><td>How long one API request may take</td></tr>
    <tr><td>`apiHeaders`</td><td>`api_headers`</td><td>none</td><td>Extra headers sent with every API request</td></tr>
    <tr><td>`proxy`</td><td>`proxy`</td><td>none</td><td>Proxy for every request, including those to the sandbox. In TypeScript it is accepted on `Sandbox.create` and `Sandbox.connect` only, and carries to the sandbox from there; Python accepts it on every call</td></tr>
    <tr><td>`validateApiKey`</td><td>`validate_api_key`</td><td>`true`</td><td>Set to `false` to skip the key shape check in TypeScript. The Python parameter is deprecated and has no effect</td></tr>
    <tr><td>`signal`</td><td>—</td><td>none</td><td>An `AbortSignal` that cancels the request. TypeScript only, and not accepted by `Sandbox.list` — pass it to `paginator.nextItems({ signal })` instead</td></tr>
  </tbody>
</table>
</div>

`requestTimeoutMs` is milliseconds in TypeScript and `request_timeout` is
seconds in Python; `0` disables the request timeout in both. Neither bounds
the sandbox's own lifetime — that is `timeoutMs` or `timeout` on create,
covered in [Timeouts](/docs/sandbox/timeouts).

Logging is a separate option rather than a variable. Pass `logger` to
`Sandbox.create` or `Sandbox.connect`, and the sandbox keeps using it for every
later request. In TypeScript it accepts anything shaped like `console`; in
Python it is a standard library `logging.Logger`. Without one, neither SDK logs
requests.

The TypeScript SDK also reads `IMPELLO_ACCESS_TOKEN` into a deprecated
`accessToken` option, and sends it as a bearer token. An API key is the only
credential you need; leave it unset. The Python SDK does not read it at all.

Next: [Templates](/docs/templates).
