> For the complete documentation index, see [llms.txt](https://docs.duku.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.duku.ai/integrations/api-keys.md).

# API Keys

Generate and use API keys for programmatic access.

API keys provide machine-to-machine access to the Duku platform.

Each API key is one base64-encoded string. It contains the OAuth2 client ID and secret. Exchange the key for a short-lived Bearer token, then use the token against the GraphQL API.

### Limits and scope

* Each key is scoped to one organisation.
* Each user can have up to **10 API keys**.
* Keys do not expire unless you set an expiry during creation.
* Organisation admins can create, view, and revoke keys. Rotation is available via the API.
* A key inherits the roles of the user who created it, so a key made by an admin can perform admin operations. Create keys from a user whose role matches what the integration needs.
* Listing, creating, rotating and revoking keys are all organisation-admin operations - a member cannot see the key list.
* Rotating a key returns a new secret and keeps the old one valid for five minutes, so a running deploy is not cut off mid-flight.

### Generate a key

1. Open **Settings → API Keys** in Viewport
2. Click **Generate API Key**
3. Enter a label like `GitHub Actions` or `CI Pipeline`
4. Click **Generate**
5. Copy the API key.

Over the API, `generateApiKey` returns `clientId` and `clientSecret` as separate fields, plus the `roles` the key carries. The single base64 string shown in the dashboard is `clientId:clientSecret` base64-encoded for convenience - combine them yourself if you need that form. The secret is returned once and cannot be retrieved again.

{% hint style="warning" %}
The API key is shown once. Store it in your secret manager immediately.
{% endhint %}

### Exchange the key for an access token

Base64-decode the key, then split its value on the first colon.

```bash
DECODED_KEY=$(printf '%s' '<your_api_key>' | base64 --decode)
CLIENT_ID=${DECODED_KEY%%:*}
CLIENT_SECRET=${DECODED_KEY#*:}

curl -X POST https://auth.duku.ai/realms/duku/protocol/openid-connect/token \
  -d "grant_type=client_credentials" \
  -d "client_id=$CLIENT_ID" \
  -d "client_secret=$CLIENT_SECRET"
```

Example response:

```json
{
  "access_token": "eyJhbGciOi...",
  "token_type": "Bearer"
}
```

Access tokens are short-lived, typically about five minutes. Fetch a fresh token rather than caching one for a long-running job.

### Use the access token

```bash
curl -X POST https://platform.duku.ai/graphql \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ subjects { id name } }"}'
```

### Rotate a key

Rotation is available via the API only - there is no Rotate button in Viewport yet. Call the `rotateApiKey` mutation to issue a new secret for an existing key, then store the new value (it is shown once). To retire a key from the dashboard, use **Revoke** and **Generate** a replacement.

{% hint style="info" %}
Confirm how long the previous secret keeps working after rotation before you rely on a specific grace window.
{% endhint %}

### Revoke a key

1. Open **Settings → API Keys**
2. Click **Revoke**
3. The key is deleted permanently

#### Auditing keys

The `apiKeys` query returns each key's `name`, `clientId`, `createdAt`, `expiresAt` and `lastUsedAt`, so you can find keys that have stopped being used before revoking them.

### Best practices

* Use one key per integration
* Rotate keys regularly
* Never commit secrets

### See also

Use Triggering explorations from any CI for a complete Platform API walkthrough.
