---
title: API Keys Reference
path: reference/api-keys
status: published
---

# API Keys Reference

API keys are the authentication method for machine clients. For the guide, see [Authentication](../concepts/authentication.md).

**Base path:** `/api/v1/api-keys/`
**Auth:** JWT only — API keys cannot manage themselves.

## GET /api/v1/api-keys/

List API keys owned by or delegated to the caller.

**Query parameters:**

| Param | Type | Notes |
|-------|------|-------|
| `include_revoked` | boolean | Include revoked keys (default false) |
| `page`, `page_size` | integer | Standard pagination |

**Response:**

```json
{
  "data": [
    {
      "id": "key_abc123",
      "name": "ci-production",
      "description": "CI pipeline key",
      "key_prefix": "sdk_live_ab",
      "status": "active",
      "permission_source": "user",
      "permission_source_id": "u_xyz",
      "scopes": ["domains:read", "records:write"],
      "rate_limit": 1000,
      "ip_whitelist": ["10.0.0.0/8"],
      "expires_at": null,
      "last_used_at": "2026-04-23T09:15:00Z",
      "last_used_ip": "10.0.1.42",
      "use_count": 1247,
      "created_at": "2026-01-10T00:00:00Z"
    }
  ],
  "total": 8
}
```

The secret is never returned after creation — only `key_prefix` (the first few characters) for identification.

## POST /api/v1/api-keys/

Create a new API key. The full secret is returned **once** in this response.

**Request:**

| Field | Type | Required | Notes |
|-------|------|---------|-------|
| `name` | string | Yes | Label |
| `description` | string | No | |
| `permission_source` | string | Yes | `user` or `group` |
| `permission_source_id` | string | Yes | User or group ID |
| `scopes` | array | No | Scope strings (subset of source's permissions) |
| `rate_limit` | integer | No | Requests per minute |
| `ip_whitelist` | array | No | CIDR blocks |
| `expires_at` | timestamp | No | |

**Response:**

```json
{
  "id": "key_abc123",
  "name": "ci-production",
  "key": "sdk_live_f7a9c...",
  "key_prefix": "sdk_live_f7",
  "permission_source": "user",
  "permission_source_id": "u_xyz",
  "status": "active",
  "scopes": [],
  "rate_limit": null,
  "ip_whitelist": [],
  "created_at": "2026-04-23T10:00:00Z"
}
```

Store `key` in a secret manager immediately — ScaiDNS does not store it in plaintext.

## GET /api/v1/api-keys/{key_id}

Get metadata for a single key (never the secret).

## PATCH /api/v1/api-keys/{key_id}

Update metadata. Editable fields: `name`, `description`, `rate_limit`, `ip_whitelist`, `expires_at`.

Cannot change the permission source or the scopes after creation — create a new key if the permission model needs to change.

## POST /api/v1/api-keys/{key_id}/regenerate

Invalidate the current secret and return a new one.

**Response:** Same shape as create — includes the new `key`.

Any client using the old secret will start getting `401` immediately. Coordinate the rotation with deploy.

## POST /api/v1/api-keys/{key_id}/revoke

Temporarily disable a key without deleting it.

**Request:** `{"reason": "suspected compromise"}` (optional).

**Response:** Key with `status: "revoked"`.

Revoked keys can be re-activated. Revoke is the right action when the key might come back.

## POST /api/v1/api-keys/{key_id}/activate

Re-enable a revoked key.

**Response:** Key with `status: "active"`.

## DELETE /api/v1/api-keys/{key_id}

Permanently delete a key. Irreversible.

**Response:** `204 No Content`.

## GET /api/v1/api-keys/permission-sources

List users and groups available as permission sources. Useful when building UIs for key creation.

**Response:**

```json
{
  "users": [
    {"id": "u_abc", "email": "user@example.com", "name": "User Name"}
  ],
  "groups": [
    {"id": "g_abc", "name": "DNS Admins", "member_count": 3}
  ]
}
```

## Permission sources

| Source | Behavior |
|--------|----------|
| `user` | Key inherits from the named user. If the user is disabled, the key loses access |
| `group` | Key inherits from the group. Adding/removing members changes what the key can do |

Keys cannot be platform admins even if the owning user is. This is a deliberate restriction — platform-level actions require a human session.

## Statuses

| Status | Meaning |
|--------|---------|
| `active` | Normal; accepts requests |
| `revoked` | Temporarily disabled; rejects with `401` |
| `expired` | `expires_at` passed; rejects with `401` |
| `deleted` | Gone; cannot be restored |

## Rate limiting

If `rate_limit` is set, the key gets that many requests per minute. Exceeding returns `429` with a `Retry-After` header.

Rate limits are per-key, not per-caller. Distribute load across multiple keys if you need more throughput.

## IP whitelisting

If `ip_whitelist` is non-empty, requests from outside those CIDR blocks return `403`. Whitelisting is evaluated against the direct source IP — be careful when running behind load balancers that rewrite source IPs.

## Audit

Every API-key-authenticated action is logged with the key ID. See [Audit Log](./audit-log.md).

## Error codes

| Status | Meaning |
|--------|---------|
| `401` | Key invalid, expired, or revoked |
| `403` | Key lacks required permission (from its source), or IP not whitelisted |
| `404` | Key not found |
| `429` | Rate limit exceeded |

## Related

- [Authentication](../concepts/authentication.md) — when to use keys vs JWTs.
- [Permissions and Access](../concepts/permissions-and-access.md) — how key permissions resolve.
