# Prism DNS Automation API Reference

Complete API reference for programmatic DNS management using API tokens.

**Base URL:** `https://your-prism-server/api`
**Interactive Docs:** `https://your-prism-server/api/docs` (Swagger UI)
**OpenAPI Spec:** `https://your-prism-server/api/openapi.json`

---

## Table of Contents

- [Authentication](#authentication)
- [Quick Start](#quick-start)
- [Error Handling](#error-handling)
- [Rate Limits](#rate-limits)
- [DNS Zones](#dns-zones)
- [DNS Records](#dns-records)
- [DNS Search & Export](#dns-search--export)
- [DNS Import](#dns-import)
- [DNSSEC](#dnssec)
- [Token Management](#token-management)
- [Public Endpoints](#public-endpoints)

---

## Authentication

The API supports two authentication methods via the `Authorization` header:

| Method | Format | Use Case |
|--------|--------|----------|
| JWT Bearer Token | `Authorization: Bearer <jwt-token>` | Web UI sessions |
| API Token | `Authorization: Bearer <api-token>` | Automation / CI-CD |

### Creating an API Token

**Option A — Web UI:** Go to Settings > Automation Tokens > Generate Token. Copy the token immediately; it is only shown once.

**Option B — API (requires JWT):** See [Create Token](#create-token).

### Token Scoping

- API tokens are **scoped to the owning user's zones**. You can only view and modify DNS zones that belong to your account.
- Tokens are 32-character alphanumeric strings, stored as bcrypt hashes.
- Tokens can have an optional expiration (1-365 days).
- Revoked or expired tokens return `401 Unauthorized`.

### Which Endpoints Accept API Tokens?

| Endpoint Group | API Token | JWT |
|----------------|-----------|-----|
| DNS Zones (`/api/dns/zones`) | Yes | Yes |
| DNS Records (`/api/dns/zones/*/records`) | Yes | Yes |
| DNS Search/Export/Import | Yes | Yes |
| DNSSEC (`/api/dns/zones/*/dnssec`) | Yes | Yes |
| Token Management (`/api/v1/tokens`) | **No** | Yes |

---

## Quick Start

### 1. Create a token (from the web UI or via JWT)

```bash
# If using JWT — first login:
JWT=$(curl -s -X POST https://prism.example.com/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"yourpassword"}' | jq -r '.access_token')

# Then create an automation token:
TOKEN=$(curl -s -X POST https://prism.example.com/api/v1/tokens \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-automation-token","expires_in_days":90}' | jq -r '.token')

echo "Save this token: $TOKEN"
```

### 2. List your DNS zones

```bash
curl -s https://prism.example.com/api/dns/zones?page=1&limit=10 \
  -H "Authorization: Bearer $TOKEN" | jq
```

### 3. Create a DNS record

```bash
curl -s -X POST https://prism.example.com/api/dns/zones/example.com./records \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "www",
    "type": "A",
    "ttl": 3600,
    "records": [{"content": "93.184.216.34"}]
  }' | jq
```

---

## Error Handling

All errors return a JSON body with this structure:

```json
{
  "detail": "Human-readable error message",
  "error_type": "http_error",
  "status_code": 404
}
```

### Common Error Codes

| Code | Meaning | When |
|------|---------|------|
| 400 | Bad Request | Invalid input (missing fields, bad zone name) |
| 401 | Unauthorized | Missing, invalid, expired, or revoked token |
| 403 | Forbidden | You don't own this zone |
| 404 | Not Found | Zone or record doesn't exist (or you don't own it) |
| 409 | Conflict | Zone already exists |
| 422 | Validation Error | Request body fails schema validation |
| 429 | Rate Limited | Too many requests (see rate limits) |
| 500 | Server Error | Unexpected internal error |
| 503 | Service Unavailable | PowerDNS backend is unreachable |

---

## Rate Limits

Rate limits are per-user. When exceeded, the API returns `429 Too Many Requests`.

| Endpoint Group | Limit |
|----------------|-------|
| List / Get / Search | 100-200/minute |
| Create / Update | 50/minute |
| Delete zones | 30/minute |
| Export | 30/minute |
| Import | 10/minute |
| Token creation | 10 tokens/hour |
| Revoke all tokens | 1/hour |

---

## DNS Zones

### List Zones

List your DNS zones with pagination, search, and sorting.

```
GET /api/dns/zones
```

**Query Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `page` | int | 1 | Page number (>= 1) |
| `limit` | int | 50 | Items per page (1-500) |
| `search` | string | — | Filter zone names (substring match) |
| `sort` | string | `name` | Sort field: `name`, `type`, `serial` |
| `order` | string | `asc` | Sort order: `asc`, `desc` |

**Response:**

```json
{
  "zones": [
    {
      "name": "example.com.",
      "kind": "Native",
      "serial": 2024022201,
      "record_count": 12,
      "status": "Active",
      "rrsets": [...]
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 3,
    "pages": 1
  }
}
```

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones?page=1&limit=10&search=example" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Search Zones

Search zones by name pattern with optional filters.

```
GET /api/dns/zones/search
```

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `q` | string | Yes | Search query (min 1 char) |
| `zone_type` | string | No | Filter: `Native`, `Master`, `Slave` |
| `hierarchy_level` | int | No | Filter by hierarchy level (>= 0) |
| `limit` | int | No | Max results (1-500, default 100) |

**Response:**

```json
{
  "query": "example",
  "total": 2,
  "zones": [...],
  "filters": {
    "zone_type": null,
    "hierarchy_level": null
  }
}
```

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/search?q=example&zone_type=Native" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Filter Zones

Filter zones by multiple criteria.

```
POST /api/dns/zones/filter
```

**Query Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `sort_by` | string | `name` | Sort field: `name`, `records`, `serial` |
| `sort_order` | string | `asc` | Sort order: `asc`, `desc` |

**Request Body:**

```json
{
  "min_records": 5,
  "max_records": 100,
  "has_dnssec": true,
  "parent_zone": "example.com.",
  "serial_after": 2024010100
}
```

All filter fields are optional.

**Response:**

```json
{
  "total": 3,
  "zones": [...],
  "filters": {"min_records": 5, "has_dnssec": true},
  "sort": {"by": "name", "order": "asc"}
}
```

**Example:**

```bash
curl -s -X POST "https://prism.example.com/api/dns/zones/filter?sort_by=records&sort_order=desc" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"min_records": 5}' | jq
```

---

### Get Zone Details

Get full details for a specific zone including all records.

```
GET /api/dns/zones/{zone_id}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `zone_id` | string | Zone name (e.g., `example.com.`) |

**Response:** Full zone object from PowerDNS with added `record_count` and `status` fields.

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/example.com." \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Create Zone

Create a new DNS zone.

```
POST /api/dns/zones
```

**Request Body:**

```json
{
  "name": "newzone.com.",
  "kind": "Native",
  "nameservers": ["ns1.example.com.", "ns2.example.com."],
  "masters": [],
  "soa_edit": "EPOCH",
  "soa_edit_api": "DEFAULT",
  "api_rectify": true,
  "account": "",
  "dnssec": false
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Zone name (must end with `.`) |
| `kind` | string | No | `Native` (default), `Master`, `Slave` |
| `nameservers` | string[] | No | Nameserver FQDNs |
| `masters` | string[] | No | Master IPs (for Slave zones) |
| `soa_edit` | string | No | SOA-EDIT setting |
| `soa_edit_api` | string | No | SOA-EDIT-API setting (default: `DEFAULT`) |
| `api_rectify` | bool | No | Enable API rectify (default: `true`) |
| `dnssec` | bool | No | Enable DNSSEC (default: `false`) |

**Response:** Created zone object.

**Example:**

```bash
curl -s -X POST "https://prism.example.com/api/dns/zones" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "newzone.com.",
    "kind": "Native",
    "nameservers": ["ns1.example.com.", "ns2.example.com."]
  }' | jq
```

---

### Update Zone

Update an existing zone's configuration. To update records, use the record endpoints.

```
PUT /api/dns/zones/{zone_id}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `zone_id` | string | Zone name |

**Request Body:** Zone configuration fields to update (kind, masters, SOA settings, etc.)

**Example:**

```bash
curl -s -X PUT "https://prism.example.com/api/dns/zones/example.com." \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"kind": "Master"}' | jq
```

---

### Delete Zone

Permanently delete a zone and all its records. This cannot be undone.

```
DELETE /api/dns/zones/{zone_id}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `zone_id` | string | Zone name |

**Example:**

```bash
curl -s -X DELETE "https://prism.example.com/api/dns/zones/oldzone.com." \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

## DNS Records

### List Zone Records

List all records in a zone with optional filtering and pagination.

```
GET /api/dns/zones/{zone_id}/records
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `zone_id` | string | Zone name |

**Query Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `record_type` | string | — | Filter by type: `A`, `AAAA`, `CNAME`, `MX`, `TXT`, etc. |
| `name` | string | — | Filter by record name |
| `page` | int | 1 | Page number |
| `limit` | int | 50 | Items per page (1-500) |

**Response:**

```json
{
  "records": [
    {
      "name": "www.example.com.",
      "type": "A",
      "ttl": 3600,
      "content": "93.184.216.34"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 12,
    "pages": 1
  }
}
```

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/example.com./records?record_type=A" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Get Record Set

Get a specific record set by name and type.

```
GET /api/dns/zones/{zone_id}/records/{name}/{record_type}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `zone_id` | string | Zone name |
| `name` | string | Record name (FQDN) |
| `record_type` | string | Record type (`A`, `AAAA`, `CNAME`, etc.) |

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/example.com./records/www.example.com./A" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Create Record

Create a new DNS record in a zone.

```
POST /api/dns/zones/{zone_id}/records
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `zone_id` | string | Zone name |

**Request Body:**

```json
{
  "name": "www",
  "type": "A",
  "ttl": 3600,
  "records": [
    {"content": "93.184.216.34"},
    {"content": "93.184.216.35"}
  ]
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Record name (relative or FQDN) |
| `type` | string | Yes | Record type: `A`, `AAAA`, `CNAME`, `MX`, `TXT`, `NS`, `SRV`, `CAA`, etc. |
| `ttl` | int | No | Time to live in seconds (default: 300) |
| `records` | object[] | Yes | Array of `{"content": "value"}` objects |

**Example:**

```bash
# Create an A record
curl -s -X POST "https://prism.example.com/api/dns/zones/example.com./records" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "app",
    "type": "A",
    "ttl": 300,
    "records": [{"content": "10.0.1.50"}]
  }' | jq

# Create an MX record
curl -s -X POST "https://prism.example.com/api/dns/zones/example.com./records" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "example.com.",
    "type": "MX",
    "ttl": 3600,
    "records": [{"content": "10 mail.example.com."}]
  }' | jq

# Create a TXT record (SPF)
curl -s -X POST "https://prism.example.com/api/dns/zones/example.com./records" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "example.com.",
    "type": "TXT",
    "ttl": 3600,
    "records": [{"content": "\"v=spf1 include:_spf.google.com ~all\""}]
  }' | jq
```

---

### Update Record

Replace all records for a specific name/type combination.

```
PUT /api/dns/zones/{zone_id}/records/{name}/{record_type}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `zone_id` | string | Zone name |
| `name` | string | Record name |
| `record_type` | string | Record type |

**Request Body:**

```json
{
  "records": [{"content": "10.0.1.100"}],
  "ttl": 600
}
```

**Example:**

```bash
curl -s -X PUT "https://prism.example.com/api/dns/zones/example.com./records/www.example.com./A" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "records": [{"content": "10.0.1.100"}, {"content": "10.0.1.101"}],
    "ttl": 600
  }' | jq
```

---

### Delete Record

Delete all records for a specific name/type combination.

```
DELETE /api/dns/zones/{zone_id}/records/{name}/{record_type}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `zone_id` | string | Zone name |
| `name` | string | Record name |
| `record_type` | string | Record type |

**Example:**

```bash
curl -s -X DELETE "https://prism.example.com/api/dns/zones/example.com./records/old.example.com./A" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

## DNS Search & Export

### Search Records

Search for DNS records across all your zones.

```
GET /api/dns/records/search
```

**Query Parameters:**

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `q` | string | Yes | Search query (min 1 char) |
| `record_type` | string | No | Filter by type: `A`, `AAAA`, `CNAME`, etc. |
| `zone` | string | No | Limit to a specific zone |
| `content` | bool | No | Search in content instead of names (default: `false`) |
| `limit` | int | No | Max results (1-500, default 100) |

**Response:**

```json
{
  "query": "www",
  "total": 5,
  "records": [
    {
      "name": "www.example.com.",
      "type": "A",
      "ttl": 3600,
      "content": "93.184.216.34",
      "zone": "example.com."
    }
  ],
  "zones_searched": 3,
  "filters": {
    "record_type": null,
    "zone": null,
    "content_search": false
  }
}
```

**Example:**

```bash
# Search by name
curl -s "https://prism.example.com/api/dns/records/search?q=www&record_type=A" \
  -H "Authorization: Bearer $TOKEN" | jq

# Search by content (e.g., find records pointing to an IP)
curl -s "https://prism.example.com/api/dns/records/search?q=10.0.1.50&content=true" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Export Records

Export all records from your zones.

```
GET /api/dns/records/export
```

**Query Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `format` | string | `json` | Export format: `json`, `csv`, `bind` |

**Response:** Depends on format — JSON returns structured data; CSV and BIND return downloadable files.

**Example:**

```bash
# Export as JSON
curl -s "https://prism.example.com/api/dns/records/export?format=json" \
  -H "Authorization: Bearer $TOKEN" | jq

# Export as CSV
curl -s "https://prism.example.com/api/dns/records/export?format=csv" \
  -H "Authorization: Bearer $TOKEN" -o dns-records.csv

# Export as BIND zone file
curl -s "https://prism.example.com/api/dns/records/export?format=bind" \
  -H "Authorization: Bearer $TOKEN" -o dns-records.zone
```

---

### Export Zones

Export zone data in various formats.

```
GET /api/dns/export/zones
```

**Query Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `format` | string | `json` | Export format: `json`, `bind`, `csv` |
| `zones` | string | — | Comma-separated zone names (all if omitted) |
| `include_dnssec` | bool | `true` | Include DNSSEC data |

**Example:**

```bash
# Export specific zones as JSON
curl -s "https://prism.example.com/api/dns/export/zones?format=json&zones=example.com.,test.com." \
  -H "Authorization: Bearer $TOKEN" | jq

# Export all zones as BIND
curl -s "https://prism.example.com/api/dns/export/zones?format=bind" \
  -H "Authorization: Bearer $TOKEN" -o zones.bind
```

---

## DNS Import

### Import Zones

Import DNS zone data.

```
POST /api/dns/import/zones
```

**Request Body:**

```json
{
  "data": "<zone data string>",
  "format": "json",
  "mode": "merge",
  "dry_run": false
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `data` | string | Yes | Zone data to import |
| `format` | string | No | `json` (default) or `bind` |
| `mode` | string | No | `merge` (default), `replace`, or `skip` |
| `dry_run` | bool | No | Preview only, don't apply (default: `false`) |

**Import Modes:**

| Mode | Behavior |
|------|----------|
| `merge` | Add new records, update existing ones |
| `replace` | Delete existing zone and recreate |
| `skip` | Skip zones that already exist |

**Example:**

```bash
curl -s -X POST "https://prism.example.com/api/dns/import/zones" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": "{\"zones\": [...]}",
    "format": "json",
    "mode": "merge",
    "dry_run": true
  }' | jq
```

---

### Preview Import

Preview what would be imported without making changes. Same parameters as Import Zones, but always runs in dry-run mode.

```
POST /api/dns/import/preview
```

**Request Body:** Same as [Import Zones](#import-zones).

**Example:**

```bash
curl -s -X POST "https://prism.example.com/api/dns/import/preview" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "data": "{\"zones\": [...]}",
    "format": "json",
    "mode": "merge"
  }' | jq
```

---

## DNSSEC

### Get DNSSEC Status

Get the current DNSSEC status for a zone.

```
GET /api/dns/zones/{zone_name}/dnssec
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `zone_name` | string | Zone name |

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/example.com./dnssec" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Enable DNSSEC

Enable DNSSEC signing for a zone. Creates KSK and ZSK automatically.

```
POST /api/dns/zones/{zone_name}/dnssec/enable
```

**Request Body:**

```json
{
  "algorithm": 13,
  "digest_type": 2,
  "nsec3": false
}
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `algorithm` | int | 13 | DNSSEC algorithm (see [List Algorithms](#list-algorithms)) |
| `digest_type` | int | 2 | DS digest type: `2` (SHA-256), `4` (SHA-384) |
| `nsec3` | bool | `false` | Use NSEC3 instead of NSEC |

**Example:**

```bash
curl -s -X POST "https://prism.example.com/api/dns/zones/example.com./dnssec/enable" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"algorithm": 13, "nsec3": false}' | jq
```

---

### Disable DNSSEC

Disable DNSSEC for a zone. Removes all signing keys.

```
POST /api/dns/zones/{zone_name}/dnssec/disable
```

**Example:**

```bash
curl -s -X POST "https://prism.example.com/api/dns/zones/example.com./dnssec/disable" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### List DNSSEC Keys

List all DNSSEC keys for a zone.

```
GET /api/dns/zones/{zone_name}/dnssec/keys
```

**Response:**

```json
{
  "zone": "example.com.",
  "keys": [
    {
      "id": 1,
      "keytype": "ksk",
      "algorithm": 13,
      "bits": 256,
      "active": true,
      "published": true
    }
  ],
  "total": 2
}
```

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/example.com./dnssec/keys" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Create DNSSEC Key

Create a new DNSSEC key for a zone.

```
POST /api/dns/zones/{zone_name}/dnssec/keys
```

**Request Body:**

```json
{
  "keytype": "zsk",
  "algorithm": 13,
  "bits": null,
  "active": true
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `keytype` | string | Yes | `ksk` (Key Signing Key) or `zsk` (Zone Signing Key) |
| `algorithm` | int | No | Algorithm number (default: 13) |
| `bits` | int | No | Key size in bits (uses algorithm default) |
| `active` | bool | No | Activate immediately (default: `true`) |

**Example:**

```bash
curl -s -X POST "https://prism.example.com/api/dns/zones/example.com./dnssec/keys" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"keytype": "zsk", "algorithm": 13, "active": true}' | jq
```

---

### Update DNSSEC Key

Activate or deactivate a DNSSEC key.

```
PUT /api/dns/zones/{zone_name}/dnssec/keys/{key_id}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `zone_name` | string | Zone name |
| `key_id` | int | Key ID |

**Request Body:**

```json
{
  "active": true
}
```

**Example:**

```bash
curl -s -X PUT "https://prism.example.com/api/dns/zones/example.com./dnssec/keys/1" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"active": false}' | jq
```

---

### Delete DNSSEC Key

Delete a DNSSEC key. Returns `204 No Content` on success.

```
DELETE /api/dns/zones/{zone_name}/dnssec/keys/{key_id}
```

**Example:**

```bash
curl -s -X DELETE "https://prism.example.com/api/dns/zones/example.com./dnssec/keys/3" \
  -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n"
```

---

### Rotate DNSSEC Keys

Perform a key rotation — creates new keys and deactivates old ones.

```
POST /api/dns/zones/{zone_name}/dnssec/rotate
```

**Request Body:**

```json
{
  "algorithm": null,
  "digest_type": 2,
  "notes": "Scheduled quarterly rotation"
}
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `algorithm` | int | null | New algorithm (uses existing if null) |
| `digest_type` | int | 2 | DS digest type |
| `notes` | string | null | Reason for rotation (logged in history) |

**Example:**

```bash
curl -s -X POST "https://prism.example.com/api/dns/zones/example.com./dnssec/rotate" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"notes": "Quarterly key rotation"}' | jq
```

---

### Get DS Records

Get DS records for registrar delegation. These records must be added to the parent zone to complete the DNSSEC chain of trust.

```
GET /api/dns/zones/{zone_name}/dnssec/ds-records
```

**Query Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `digest_type` | int | Filter by digest type: `2` (SHA-256) or `4` (SHA-384) |

**Response:**

```json
{
  "zone": "example.com.",
  "ds_records": ["12345 13 2 ABCDEF..."],
  "ds_by_digest": {
    "SHA-256": ["12345 13 2 ABCDEF..."]
  },
  "instructions": "Add these DS records to the parent zone..."
}
```

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/example.com./dnssec/ds-records" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Validate DNSSEC

Basic DNSSEC validation for a zone.

```
GET /api/dns/zones/{zone_name}/dnssec/validate
```

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/example.com./dnssec/validate" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Validate DNSSEC Cryptography

Comprehensive cryptographic validation including signature verification and chain of trust.

```
GET /api/dns/zones/{zone_name}/dnssec/validate-crypto
```

**Query Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `check_parent` | bool | `false` | Also validate parent chain of trust |

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/example.com./dnssec/validate-crypto?check_parent=true" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### Get Key History

Get the DNSSEC key operation history (creation, rotation, deletion) for a zone.

```
GET /api/dns/zones/{zone_name}/dnssec/history
```

**Query Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `limit` | int | 50 | Max history entries |

**Response:**

```json
{
  "zone": "example.com.",
  "history": [
    {
      "key_id": 1,
      "keytype": "ksk",
      "algorithm": 13,
      "operation": "created",
      "performed_by": "user-uuid",
      "notes": "KSK created when enabling DNSSEC",
      "created_at": "2024-02-22T10:00:00Z"
    }
  ],
  "total": 5
}
```

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/example.com./dnssec/history?limit=20" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### List Algorithms

List all supported DNSSEC signing algorithms.

```
GET /api/dns/zones/dnssec/algorithms
```

**Response:**

```json
{
  "algorithms": [
    {
      "number": 13,
      "name": "ECDSAP256SHA256",
      "description": "ECDSA Curve P-256 with SHA-256",
      "recommended": true,
      "default_bits": 256
    },
    {
      "number": 14,
      "name": "ECDSAP384SHA384",
      "description": "ECDSA Curve P-384 with SHA-384",
      "recommended": true,
      "default_bits": 384
    }
  ]
}
```

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/dnssec/algorithms" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

### List Digest Types

List supported DS record digest types.

```
GET /api/dns/zones/dnssec/digest-types
```

**Response:**

```json
{
  "digest_types": [
    {
      "number": 2,
      "name": "SHA-256",
      "description": "SHA-256 digest",
      "recommended": true
    },
    {
      "number": 4,
      "name": "SHA-384",
      "description": "SHA-384 digest",
      "recommended": true
    }
  ]
}
```

**Example:**

```bash
curl -s "https://prism.example.com/api/dns/zones/dnssec/digest-types" \
  -H "Authorization: Bearer $TOKEN" | jq
```

---

## Token Management

> **Note:** Token management endpoints require **JWT authentication only**. You cannot use an API token to create or manage other API tokens.

### Create Token

Generate a new API token for automation.

```
POST /api/v1/tokens
```

**Request Body:**

```json
{
  "name": "my-automation-token",
  "expires_in_days": 90
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Token name (1-255 chars) |
| `expires_in_days` | int | No | Expiration in days (1-365, null = never) |

**Response:**

```json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "my-automation-token",
  "token": "aBcDeFgHiJkLmNoPqRsTuVwXyZ012345",
  "expires_at": "2024-05-22T10:30:00Z",
  "created_at": "2024-02-22T10:30:00Z"
}
```

> **Important:** The `token` field is only returned once at creation. Store it securely.

**Example:**

```bash
curl -s -X POST "https://prism.example.com/api/v1/tokens" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"name": "ci-cd-token", "expires_in_days": 90}' | jq
```

---

### List Tokens

List all your API tokens. Plain token values are never returned.

```
GET /api/v1/tokens
```

**Response:**

```json
[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "ci-cd-token",
    "last_used_at": "2024-02-22T15:00:00Z",
    "expires_at": "2024-05-22T10:30:00Z",
    "is_active": true,
    "created_at": "2024-02-22T10:30:00Z"
  }
]
```

**Example:**

```bash
curl -s "https://prism.example.com/api/v1/tokens" \
  -H "Authorization: Bearer $JWT" | jq
```

---

### Revoke Token

Revoke a specific API token. The token will immediately stop working.

```
DELETE /api/v1/tokens/{token_id}
```

**Path Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `token_id` | string | Token UUID |

**Response:**

```json
{
  "message": "Token revoked successfully",
  "token_id": "550e8400-e29b-41d4-a716-446655440000",
  "revoked_at": "2024-02-22T16:00:00Z"
}
```

**Example:**

```bash
curl -s -X DELETE "https://prism.example.com/api/v1/tokens/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer $JWT" | jq
```

---

### Revoke All Tokens

Emergency endpoint to revoke all your active API tokens at once. Rate limited to once per hour.

```
POST /api/v1/tokens/revoke-all
```

**Response:**

```json
{
  "message": "Revoked 3 tokens",
  "revoked_count": 3
}
```

**Example:**

```bash
curl -s -X POST "https://prism.example.com/api/v1/tokens/revoke-all" \
  -H "Authorization: Bearer $JWT" | jq
```

---

## Public Endpoints

These endpoints do not require authentication.

### DNS Health Check

Check PowerDNS connectivity.

```
GET /api/dns/health
```

**Response:**

```json
{
  "status": "healthy",
  "powerdns": "connected"
}
```

---

### DNS Configuration

Get DNS service configuration (feature flags and settings).

```
GET /api/dns/config
```

**Response:**

```json
{
  "powerdns_enabled": true,
  "feature_flag_percentage": 100,
  "fallback_to_mock": false,
  "api_url": "http://powerdns-server:8053/api/v1",
  "default_zone": "managed.prism.local."
}
```
