# API Keys (/docs/api-keys)

Daytona API keys authenticate requests from the SDKs, the CLI, and the API to the resources in a [project](/docs/projects). To pass a key, see [authentication](/docs/authentication).

[Organization API keys](#organization-api-keys) cover organization-level operations that are not tied to a project.

Every project API key belongs to exactly one project and carries an explicit list of [permissions](#scopes); its requests are confined to both. Keys can carry an expiry. Revocation applies to new requests; a regional proxy can accept a revoked key for up to 30 seconds.

<ApiKeysVisual />

<Callout type="caution" title="Daytona v0 key sync">
In an organization kept in sync with Daytona v0, a key that a member creates in the default project under a name other than `default` is also copied to Daytona v0. The v0 copy can read every sandbox, snapshot, and registry in the organization, whatever the key's permissions. To keep a key off Daytona v0, set `sync_to_v0` to `false` when you create it, or update the key later.
</Callout>

## Create an API key [#create-an-api-key]

Create an API key to authenticate Daytona SDK and API requests.

1. Go to [Daytona Dashboard ↗](https://app.daytona.com/dashboard/keys)
2. Select the project in the header switcher
3. Click <Button>Create API Key</Button>
4. Enter a <Button>Key Name</Button>, optionally set an expiry, and [select permissions](#scopes)
5. Click <Button>Create API Key</Button>
6. Copy the API key to your clipboard and use it to authenticate requests

<Tabs syncKey="language">
<TabItem label="CLI" icon="seti:shell">

```bash
daytona key mint --project PROJECT_ID \
  --name my-api-key \
  --permission write:sandboxes,delete:sandboxes \
  --expires 2027-01-01T00:00:00Z
```

| **Flag**       | **Type** | **Description**                                                                 |
| -------------- | -------- | ------------------------------------------------------------------------------- |
| `--permission` | string | A comma-separated list of permissions.                                          |
| `--expires`    | string | An RFC 3339 timestamp or a Go duration such as `720h`, counted from now.        |

<Callout type="note" title="Notes">
- The request requires the `write:project_api_keys` permission.
- `daytona key mint` prints the plaintext token once, when the key is created.
</Callout>

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/projects/PROJECT_ID/api-keys' \
  --request POST \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "my-api-key",
  "project_permissions": ["write:sandboxes", "delete:sandboxes"],
  "expires_at": "2027-01-01T00:00:00Z"
}'
```

```json
{
  "id": "KEY_ID",
  "name": "my-api-key",
  "project_id": "PROJECT_ID",
  "key_prefix": "dtn_...",
  "token": "YOUR_API_KEY",
  "project_permissions": ["read:sandboxes", "write:sandboxes", "delete:sandboxes"],
  "expires_at": "2027-01-01T00:00:00Z",
  "service_account_id": null
}
```

| **Field**                 | **Description**                                                                                                            |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `name`                | Display name for the key. Optional; defaults to `default`. Any other name must be unique among the project's live keys. |
| `project_permissions` | Required, non-empty list of [permissions](#scopes). Unknown values are rejected. |
| `expires_at`          | Optional RFC 3339 timestamp in the future. Omit for a key that never expires.                                              |
| `service_account_id`  | Optional. Mint the key for a [service account](#service-accounts) instead of yourself. Service account keys must be named. |
| `sync_to_v0`          | Optional. Copies the key to Daytona v0. Defaults to `true`. Applies only to keys a person mints in the `default` project; see [update an API key](#update-an-api-key). |

The response has the following fields:

| **Field**             | **Description**                                                                         |
| --------------------- | --------------------------------------------------------------------------------------- |
| `id`                  | The API key ID.                                                                         |
| `name`                | The API key name.                                                                       |
| `project_id`          | The project the key belongs to.                                                         |
| `key_prefix`          | The leading characters of the token, shown in lists to recognize the key.               |
| `token`               | The secret token. Only the create response returns it, so store it then.                |
| `project_permissions` | The permissions of the key, with the read permission each write or delete implies.      |
| `expires_at`          | When the key expires, or `null` when it does not expire.                                |
| `service_account_id`  | The service account that owns the key, or `null` for a key you own.                     |

</TabItem>
</Tabs>

## Credentials [#credentials]

Daytona accepts two bearer credentials, named differently in this page's API examples:

| **Credential** | **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **API KEY**    | A project API key created as described above. It authenticates SDK, toolbox, and sandbox requests, and the few management routes that accept it, such as [getting the current key](#get-current-api-key). This is the credential the SDKs and the `DAYTONA_API_KEY` variable carry. See [authentication](/docs/authentication) for how to pass it to the SDKs, the CLI, and API requests.                                                                                             |
| **OIDC TOKEN** | The session token of a signed-in user (an OIDC bearer JWT, the credential the dashboard uses). Management routes that act on behalf of a person require it and reject API keys with `401`: creating, listing, and revoking keys, service accounts, [organizations](/docs/organizations), [projects](/docs/projects), and [billing](/docs/billing). Obtain it by signing in with the dashboard or `daytona login`; the CLI commands in those sections use the saved session for you. |

[Organization API keys](#organization-api-keys) are a third kind, scoped to an organization rather than a project; that section lists the scopes they can hold.

## Scopes [#scopes]

| **Resource**       | **Scope**               | **Description**                                                    |
| ------------------ | ----------------------- | ------------------------------------------------------------------ |
| Sandboxes          | **`read:sandboxes`**    | View sandboxes                                                     |
|                    | **`write:sandboxes`**   | Create/modify sandboxes and use the toolbox                        |
|                    | **`delete:sandboxes`**  | Delete sandboxes                                                   |
| Snapshots          | **`read:snapshots`**    | View [snapshots](/docs/snapshots)                               |
|                    | **`write:snapshots`**   | Create/modify snapshots                                            |
|                    | **`delete:snapshots`**  | Delete snapshots                                                   |
| Images             | **`read:images`**       | View [images](/docs/images)                                     |
|                    | **`write:images`**      | Create/modify images                                               |
|                    | **`delete:images`**     | Delete images                                                      |
| Volumes            | **`read:volumes`**      | View [volumes](/docs/volumes)                                   |
|                    | **`write:volumes`**     | Create/modify volumes                                              |
|                    | **`delete:volumes`**    | Delete volumes                                                     |
| Registries         | **`read:registries`**   | View [registries](/docs/registries)                             |
|                    | **`write:registries`**  | Create/modify registries                                           |
|                    | **`delete:registries`** | Delete registries                                                  |
| Secrets            | **`read:secrets`**      | View [secrets](/docs/secrets)                                   |
|                    | **`write:secrets`**     | Create/modify secrets                                              |
|                    | **`delete:secrets`**    | Delete secrets                                                     |
| Spending           | **`read:spending`**     | View project spending                                              |
| Limits             | **`read:limits`**       | View [organization limits](/docs/organizations#limits)          |
| Functions          | **`read:functions`**    | View functions                                                     |
|                    | **`write:functions`**   | Deploy and configure functions                                     |
|                    | **`delete:functions`**  | Delete functions                                                   |
| Invocations        | **`read:invocations`**  | View invocation history                                            |
|                    | **`write:invocations`** | Invoke functions                                                   |

## Operations [#operations]

### List API keys [#list-api-keys]

List the API keys of a project. The list never contains tokens or revoked keys; an expired key stays in the list until Daytona deletes it. Daytona deletes revoked and expired keys 7 days after they stop working.

<Tabs syncKey="language">
<TabItem label="CLI" icon="seti:shell">

```bash
daytona key list --project PROJECT_ID
```

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/projects/PROJECT_ID/api-keys' \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN'
```

```json
{
  "project_api_keys": [
    {
      "id": "KEY_ID",
      "name": "my-api-key",
      "key_prefix": "dtn_...",
      "created_at": "2026-09-10T12:00:00Z",
      "revoked_at": null,
      "last_used_at": "2026-09-11T08:30:00Z",
      "project_permissions": ["read:sandboxes", "write:sandboxes", "delete:sandboxes"],
      "expires_at": "2027-01-01T00:00:00Z",
      "service_account_id": null,
      "service_account_name": null
    }
  ],
  "next_cursor": null
}
```

</TabItem>
</Tabs>

### Get API key [#get-api-key]

<Tabs syncKey="language">
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/projects/PROJECT_ID/api-keys/KEY_ID' \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN'
```

</TabItem>
</Tabs>

### Get current API key [#get-current-api-key]

Get the API key that authenticates the request, including the project and organization it acts in and the permissions it carries. The response never contains the token.

<Tabs syncKey="language">
<TabItem label="Python" icon="seti:python">

```python
from daytona import Daytona

daytona = Daytona()
key = daytona.v2.current_api_key()
print(key.name, key.context.project_name, key.context.org_name)
print(key.project_permissions)
```

</TabItem>
<TabItem label="TypeScript" icon="seti:typescript">

```typescript
import { Daytona } from '@daytona/sdk'

const daytona = new Daytona()
const key = await daytona.v2.currentApiKey()
console.log(key.name, key.context.projectName, key.context.orgName)
console.log(key.projectPermissions)
```

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/api-keys/current' \
  --header 'Authorization: Bearer YOUR_API_KEY'
```

</TabItem>
</Tabs>

### Exchange a token [#exchange-a-token]

Exchange a signed-in user's session token for a short-lived access token scoped to one project. The access token carries the user's own permissions in that project and authenticates workload requests in place of a project API key. Organization API keys cannot be exchanged.

<Tabs syncKey="language">
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/auth/token' \
  --request POST \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
  "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
  "subject_token": "YOUR_OIDC_TOKEN",
  "subject_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "project_id": "PROJECT_ID"
}'
```

```json
{
  "access_token": "eyJ...",
  "issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
  "token_type": "Bearer",
  "expires_in": 300
}
```

| **Field**            | **Type** | **Description**                                                                                                     |
| -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| `grant_type`         | string   | Required. `urn:ietf:params:oauth:grant-type:token-exchange`.                                                        |
| `subject_token`      | string   | Required. Must be the same token as the bearer in the `Authorization` header; otherwise the request returns `400`. |
| `subject_token_type` | string   | Required. `urn:ietf:params:oauth:token-type:access_token` or `urn:ietf:params:oauth:token-type:jwt`.                |
| `project_id`         | string   | Required. The project the access token is scoped to. An unknown project, or one you are not a member of, returns `404`. |

<Callout type="note" title="Notes">
- `expires_in` is the access token lifetime in seconds.
- Exchange again for a new access token when the current access token expires.
- The request must omit `scope` and `resource`.
</Callout>

</TabItem>
</Tabs>

### Update an API key [#update-an-api-key]

Update whether an API key is copied to Daytona v0. A Daytona v0 copy can read every sandbox, snapshot, and registry of the organization on Daytona v0, because Daytona v0 has no read permissions. Turning the copy off deletes the Daytona v0 key shortly afterwards; the key keeps working on Daytona v2.

<Tabs syncKey="language">
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/projects/PROJECT_ID/api-keys/KEY_ID' \
  --request PATCH \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{ "sync_to_v0": false }'
```

| **Field**        | **Type**      | **Description**                                                              |
| ---------------- | ------------- | ---------------------------------------------------------------------------- |
| `sync_to_v0` | boolean | Required. `false` deletes the Daytona v0 copy; `true` copies the key again. |

<Callout type="note" title="Notes">
- Updating a key you created requires the `write:project_api_keys` permission.
- Updating a key that another member created requires the `delete:project_api_keys` permission.
- Updating a service account key requires the `delete:service_accounts` permission.
- The key must be one that a person minted in the `default` project of an organization that syncs with Daytona v0, under a name other than `default`.
- Daytona copies only these keys to Daytona v0.
</Callout>

</TabItem>
</Tabs>

### Revoke an API key [#revoke-an-api-key]

Revoke an API key. Revocation cannot be undone. New requests made with the key fail, except at a regional proxy, which can accept the key for up to 30 seconds after revocation. Daytona deletes the revoked key 7 days later.

1. Go to [Daytona Dashboard ↗](https://app.daytona.com/dashboard/keys)
2. Click <Button>Revoke</Button> next to the API key you want to revoke
3. Confirm the revocation

<Tabs syncKey="language">
<TabItem label="CLI" icon="seti:shell">

```bash
daytona key revoke KEY_ID
```

<Callout type="note" title="Notes">
- Revoking a key you created requires the `write:project_api_keys` permission.
- Revoking a key that another member created requires the `delete:project_api_keys` permission.
- Revoking a service account key requires the `delete:service_accounts` permission.
</Callout>

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/projects/PROJECT_ID/api-keys/KEY_ID' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN'
```

<Callout type="note" title="Notes">
- Revoking a key you created requires the `write:project_api_keys` permission.
- Revoking a key that another member created requires the `delete:project_api_keys` permission.
- Revoking a service account key requires the `delete:service_accounts` permission.
</Callout>

</TabItem>
</Tabs>

## Service accounts [#service-accounts]

Service accounts are non-human identities in your organization. Use them to own API keys for automation so that the keys are not tied to a person's membership.

### Create a service account [#create-a-service-account]

<Tabs syncKey="language">
<TabItem label="CLI" icon="seti:shell">

```bash
daytona service-account create my-service-account --role developer --org ORG_ID
```

| **Flag** | **Type** | **Description**                       |
| -------- | -------- | ------------------------------------- |
| `--role` | string | Sets the roles, as a comma-separated list of role slugs. |

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/orgs/ORG_ID/service-accounts' \
  --request POST \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "my-service-account",
  "roles": ["developer"]
}'
```

| **Field**     | **Type**   | **Description**                                                                                                                 |
| ------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `name`        | string   | Required. At most 64 letters, digits, `-`, or `_`, unique in the organization.                                                  |
| `description` | string   | Optional, up to 512 characters.                                                                                                 |
| `roles`       | string\[\] | Required. Must hold at least one role. The `admin` role is refused, and every permission the roles grant must be one you hold yourself. |

<Callout type="note" title="Notes">
- The request requires the `write:service_accounts` permission.
- Personal organizations cannot have service accounts.
</Callout>

</TabItem>
</Tabs>

### Create a service account key [#create-a-service-account-key]

Create an API key owned by a service account. The key keeps working when its creator leaves the organization. Outside the default project, the service account must first be [added to the project](/docs/projects#add-project-service-accounts), and the key can carry only permissions you and the service account both hold.

1. Go to [Daytona Dashboard ↗](https://app.daytona.com/dashboard/keys)
2. Click <Button>Create API Key</Button>
3. Under <Button>Owner</Button>, select the service account
4. Enter a <Button>Key Name</Button>, [select permissions](#scopes), and click <Button>Create API Key</Button>

<Tabs syncKey="language">
<TabItem label="CLI" icon="seti:shell">

```bash
daytona key mint --project PROJECT_ID \
  --name my-service-account-key \
  --permission write:sandboxes \
  --service-account SERVICE_ACCOUNT_ID \
  --org ORG_ID
```

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/projects/PROJECT_ID/api-keys' \
  --request POST \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "my-service-account-key",
  "project_permissions": ["write:sandboxes"],
  "service_account_id": "SERVICE_ACCOUNT_ID"
}'
```

</TabItem>
</Tabs>

### List service accounts [#list-service-accounts]

<Tabs syncKey="language">
<TabItem label="CLI" icon="seti:shell">

```bash
daytona service-account list --org ORG_ID
```

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/orgs/ORG_ID/service-accounts' \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN'
```

</TabItem>
</Tabs>

### Get a service account [#get-a-service-account]

<Tabs syncKey="language">
<TabItem label="CLI" icon="seti:shell">

```bash
daytona service-account get SERVICE_ACCOUNT_ID
```

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/orgs/ORG_ID/service-accounts/SERVICE_ACCOUNT_ID' \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN'
```

</TabItem>
</Tabs>

### Update a service account [#update-a-service-account]

Update a service account's name, description, or roles. Fields you leave out stay unchanged, and new roles replace the whole role set. When the new roles are narrower, Daytona revokes the account's project API keys that exceed them.

<Tabs syncKey="language">
<TabItem label="CLI" icon="seti:shell">

```bash
# The CLI updates the name (--name) and roles (--role); it cannot set the description
daytona service-account update SERVICE_ACCOUNT_ID --role maintainer
```

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/orgs/ORG_ID/service-accounts/SERVICE_ACCOUNT_ID' \
  --request PATCH \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
  "description": "CI runner",
  "roles": ["maintainer"]
}'

# Preview the change without applying it: { "revoked_api_keys": ..., "revoked_preview_grants": ... }
curl 'https://api.daytona.com/orgs/ORG_ID/service-accounts/SERVICE_ACCOUNT_ID/update-preview' \
  --request POST \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
  "roles": ["maintainer"]
}'
```

<Callout type="note" title="Notes">
- The requests require the `write:service_accounts` permission.
- The request requires at least one of `name`, `description`, or `roles`.
</Callout>

</TabItem>
</Tabs>

### Archive a service account [#archive-a-service-account]

Archive a service account. This revokes every project API key it owns. An archived account cannot be updated, added to projects, or given new keys, and lists do not contain it.

<Tabs syncKey="language">
<TabItem label="CLI" icon="seti:shell">

```bash
daytona service-account delete SERVICE_ACCOUNT_ID
```

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
curl 'https://api.daytona.com/orgs/ORG_ID/service-accounts/SERVICE_ACCOUNT_ID' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN'
```

</TabItem>
</Tabs>

## Organization API keys [#organization-api-keys]

Organization API keys authenticate organization-level operations that are not tied to a project.

| **Resource**        | **Scope**                                                     | **Description**                             |
| ------------------- | ------------------------------------------------------------- | ------------------------------------------- |
| Audit logs          | **`read:audit`**                                              | View [audit logs](/docs/audit-logs)      |
| Billing             | **`read:billing`**, **`write:billing`**                       | View and manage [billing](/docs/billing) |
| Spending            | **`read:spending`**                                           | View organization spending                  |
| Telemetry           | **`read:telemetry`**                                          | Read sandbox logs, traces, and metrics      |

<Tabs syncKey="language">
<TabItem label="CLI" icon="seti:shell">

```bash
# Create
daytona org-key mint --org ORG_ID --name my-org-key --permission read:audit

# List
daytona org-key list --org ORG_ID

# Revoke
daytona org-key revoke KEY_ID
```

| **Flag** | **Type** | **Description** |
| -------- | -------- | --------------- |
| `--name` | string | This flag is required.       |

<Callout type="note" title="Notes">
- Listing and getting organization API keys requires the `read:org_api_keys` permission.
- Creating an organization API key requires the `write:org_api_keys` permission.
- Revoking an organization API key requires the `delete:org_api_keys` permission.
- `daytona org-key mint` prints the plaintext token once, when the key is created.
- `daytona org-key revoke` asks for confirmation at a terminal and requires `--yes` otherwise.
</Callout>

</TabItem>
<TabItem label="API" icon="seti:json">

```bash
# Create
curl 'https://api.daytona.com/orgs/ORG_ID/api-keys' \
  --request POST \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "my-org-key",
  "org_permissions": ["read:audit"]
}'

# List
curl 'https://api.daytona.com/orgs/ORG_ID/api-keys' \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN'

# Get
curl 'https://api.daytona.com/orgs/ORG_ID/api-keys/KEY_ID' \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN'

# Revoke
curl 'https://api.daytona.com/orgs/ORG_ID/api-keys/KEY_ID' \
  --request DELETE \
  --header 'Authorization: Bearer YOUR_OIDC_TOKEN'
```

| **Field**         | **Type**   | **Description**                                              |
| ----------------- | ---------- | ------------------------------------------------------------ |
| `name`            | string   | Required.                                                    |
| `org_permissions` | string\[\] | Required. A non-empty list of scopes from the table above.   |
| `expires_at`      | string   | Optional. Must be in the future.                             |

The create response has the following fields:

| **Field**         | **Description**                                                          |
| ----------------- | ------------------------------------------------------------------------ |
| `id`              | The API key ID.                                                          |
| `key_prefix`      | The leading characters of the token, shown in lists to recognize the key. |
| `token`           | The secret token. Only the create response returns it, so store it then. |
| `org_permissions` | The organization permissions of the key.                                 |
| `expires_at`      | When the key expires, or `null` when it does not expire.                 |

</TabItem>
</Tabs>

## Permissions [#permissions]

API keys and service accounts are managed with organization permissions, which you can grant through an [organization role](/docs/organizations#roles).

A key can hold only the permissions its creator holds. The [audit logs](/docs/audit-logs) record key creates, updates, and revocations, and service account changes.

| **Permission**                | **Grants**                                                          |
| ----------------------------- | ------------------------------------------------------------------- |
| **`read:project_api_keys`**   | List and get project API keys                                       |
| **`write:project_api_keys`**  | Create project API keys, and update and revoke the keys you created |
| **`delete:project_api_keys`** | Update and revoke keys that other members created                   |
| **`read:org_api_keys`**       | List and get organization API keys                                  |
| **`write:org_api_keys`**      | Create organization API keys                                        |
| **`delete:org_api_keys`**     | Revoke organization API keys                                        |
| **`read:service_accounts`**   | List and get service accounts                                       |
| **`write:service_accounts`**  | Create and update service accounts, and create their keys together with `write:project_api_keys` |
| **`delete:service_accounts`** | Archive service accounts, and update and revoke their keys          |
