# DNSSEC

> Publish, read and remove DS records at the registry, in the order that keeps the domain online.

DNSSEC cryptographically signs your DNS so resolvers can detect tampering. You generate the keys at your DNS provider; this API publishes the resulting **DS records** at the registry.

:::caution[Get the order right or the domain goes dark]
DNSSEC is the easiest way to take a working domain offline. The rule is simple: **the keys must be live in DNS before the DS record is published at the registry, and the DS record must be removed before the keys go away.**

Enabling: publish keys at your DNS provider → then `POST /dnssec` here.\
Disabling or migrating: `POST /dnssec/disable` here → wait for confirmation → then change DNS.
:::

## GET /api/v1/domains/dns/dnssec

Current DNSSEC state, the published DS records, and what this extension accepts. **Call this before any write** — it tells you which algorithms and digest types are permitted.

**Request**

```http
GET /api/v1/domains/dns/dnssec?domainName=apiname.com
```

**Response — 200**

```json
{
  "domainName": "apiname.com",
  "isDnssecAvailable": true,
  "status": 2,
  "delegationSigned": true,
  "maxSigLife": null,
  "lastChangedAt": "2026-10-28T09:14:52",
  "dsRecords": [
    {
      "keyTag": 12345,
      "algorithm": 13,
      "digestType": 2,
      "digest": "49FD46E6C4B45C55D4AC69CBD3CD34AC1AFE51DE7D4C6F9CFBF5F8A1B2C3D4E5",
      "status": 1,
      "isAlgorithmNotRecommended": false,
      "createdAt": "2026-10-28T09:12:03",
      "activatedAt": "2026-10-28T09:14:52",
      "removedAt": null
    }
  ],
  "allowedAlgorithms": [8, 13, 14, 15, 16],
  "allowedDigestTypes": [2, 4],
  "maxDsRecordCount": 8
}
```

| Field | How to use it |
| --- | --- |
| `isDnssecAvailable` | If `false`, every write here fails with `Dnssec:10001`. Hide the DNSSEC option in your UI. |
| `delegationSigned` | **The authoritative "is it live" signal.** `status` can lag behind registry reality; this reflects what is actually published. |
| `allowedAlgorithms` | The algorithm numbers you may send. Build your dropdown from this, not from a hard-coded list — it varies by extension. |
| `allowedDigestTypes` | Same for digest types. |
| `maxDsRecordCount` | Maximum DS records. Default 8. Relevant during key rollover, when you briefly need two. |
| `dsRecords[].isAlgorithmNotRecommended` | The algorithm works but is discouraged by current standards. Plan to migrate. |

**status**

| Value | Name | Meaning |
| --- | --- | --- |
| 0 | `Unsigned` | No DS record at the registry. DNSSEC is off. |
| 1 | `PendingEnable` | Add request sent, awaiting registry confirmation. |
| 2 | `Signed` | At least one DS record is live. |
| 3 | `PendingDisable` | Removal sent, awaiting confirmation. |
| 4 | `Error` | The last DNSSEC operation failed. Read the DS records' individual statuses. |

**dsRecords\[\].status**

| Value | Name | Meaning |
| --- | --- | --- |
| 0 | `Pending` | Submitted, not confirmed. |
| 1 | `Active` | Live at the registry. |
| 2 | `Removing` | Removal in flight. |
| 3 | `Removed` | Gone. Excluded from this list. |
| 4 | `Failed` | The registry rejected it. |

### Algorithms and digest types

Allowed values vary by extension — always read them from this endpoint. For reference:

| Value | Algorithm | Status |
| --- | --- | --- |
| 8 | `RSA/SHA-256` | **Recommended** |
| 13 | `ECDSA P-256/SHA-256` | **Recommended** — the usual modern choice |
| 14 | `ECDSA P-384/SHA-384` | Recommended |
| 15 | `Ed25519` | Recommended |
| 16 | `Ed448` | Recommended |
| 5, 7 | `RSA/SHA-1 variants` | Accepted but discouraged |
| 10 | `RSA/SHA-512` | Accepted but discouraged |
| 1, 3, 6, 12 | `MD5, DSA, GOST` | **Rejected** — never accepted, even if an extension lists them |

| Value | Digest type | Required `digest` length |
| --- | --- | --- |
| 1 | `SHA-1` | **40** hex characters |
| 2 | `SHA-256` | **64** hex characters — the usual choice |
| 4 | `SHA-384` | **96** hex characters |
| 3 | `GOST` | **Rejected** |

:::note[Digest formatting is forgiving, length is not]
We strip whitespace and uppercase the value, so `"ab cd ef"` and `"ABCDEF"` are the same. But it must then be pure hexadecimal and **exactly** the length required by the `digestType` you declared. Mismatched length is the most common DNSSEC error — usually from sending a SHA-256 digest while declaring `digestType: 1`.
:::

## POST /api/v1/domains/dns/dnssec

Publishes one or more DS records at the registry, enabling DNSSEC.

**Request**

```json
{
  "domainName": "apiname.com",
  "records": [
    {
      "keyTag": 12345,
      "algorithm": 13,
      "digestType": 2,
      "digest": "49FD46E6C4B45C55D4AC69CBD3CD34AC1AFE51DE7D4C6F9CFBF5F8A1B2C3D4E5"
    }
  ]
}
```

| Field | Required | Notes |
| --- | --- | --- |
| `domainName` | required |   |
| `records` | required | At least one entry. |
| `records[].keyTag` | required | 0–65535. From your DNS provider. |
| `records[].algorithm` | required | Must be in `allowedAlgorithms`. |
| `records[].digestType` | required | Must be in `allowedDigestTypes`. |
| `records[].digest` | required | Hex, exact length for the digest type. |

**Response — 200**

```json
{
  "items": [
    {
      "keyTag": 12345,
      "operationType": 0,
      "isSuccess": true,
      "isPending": true,
      "resultMessage": "Command completed successfully; action pending"
    }
  ]
}
```

:::tip[`isPending: true` is a success, not a problem]
Many registries queue DNSSEC changes. `isSuccess: true` with `isPending: true` means the registry accepted your request and will apply it shortly. **Do not resubmit** — that produces `Dna.DomainService:Dnssec:10006` ("already exists").

Poll `GET /dnssec` until `delegationSigned` becomes `true`.
:::

`operationType`: `0` Add · `1` Remove · `2` RemoveAll · `3` Sync.

## DELETE /api/v1/domains/dns/dnssec · POST /api/v1/domains/dns/dnssec/disable

Remove one DS record, or all of them.

**DELETE — remove one record**

```json
{
  "domainName": "apiname.com",
  "keyTag": 12345,
  "algorithm": 13,
  "digestType": 2,
  "digest": "49FD46E6C4B45C55D4AC69CBD3CD34AC1AFE51DE7D4C6F9CFBF5F8A1B2C3D4E5"
}
```

The record is identified by its **full value**, not by an ID. All four fields must match exactly — copy them straight from `GET /dnssec`. Any mismatch gives `Dna.DomainService:Dnssec:10007`.

The fields travel in the **request body** of the `DELETE`. Make sure your HTTP client sends a body with `DELETE` requests; some drop it unless told otherwise.

**Response — 200, DELETE**

```json
{
  "keyTag": 12345,
  "operationType": 1,
  "isSuccess": true,
  "isPending": false,
  "resultMessage": "Command completed successfully"
}
```

**POST /dnssec/disable — remove all records**

```json
{
  "domainName": "apiname.com"
}
```

**Response — 200, disable**

```json
{
  "keyTag": null,
  "operationType": 2,
  "isSuccess": true,
  "isPending": false,
  "resultMessage": "Command completed successfully"
}
```

`operationType` says what was done: `1` one record removed, `2` all records removed. As when adding, `isSuccess: true` with `isPending: true` means the registry accepted the removal and will finish it later. Poll `GET /dnssec` rather than sending it again.

:::tip[Key rollover without downtime]
1. Generate the new key at your DNS provider and publish it alongside the old one.
2. `POST /dnssec` with the **new** DS record. Both are now live — this is why `maxDsRecordCount` is more than one.
3. Wait for `GET /dnssec` to show the new record as `Active`, then wait one more TTL cycle.
4. `DELETE /dnssec` with the **old** DS record.
5. Remove the old key at your DNS provider.

Never remove the old DS record before the new one is active.
:::

**Errors — all DNSSEC endpoints**

| Code | Meaning and fix |
| --- | --- |
| `Dna.DomainService:Dnssec:10001` | This extension does not support DNSSEC. |
| `Dna.DomainService:Dnssec:10002` | `keyTag` outside 0–65535. |
| `Dna.DomainService:Dnssec:10003` | Algorithm not permitted. Read `allowedAlgorithms`. |
| `Dna.DomainService:Dnssec:10004` | Digest type not permitted. Read `allowedDigestTypes`. |
| `Dna.DomainService:Dnssec:10005` | **The most common one.** The digest is not hex, or its length does not match the declared `digestType`. `details` states the expected length. |
| `Dna.DomainService:Dnssec:10006` | That DS record already exists, or you sent it twice in one request. |
| `Dna.DomainService:Dnssec:10007` | Delete: no record matches all four fields. Copy them from `GET /dnssec`. |
| `Dna.DomainService:Dnssec:10008` | Would exceed `maxDsRecordCount`. Remove an old record first. |
| `Dna.DomainService:Dnssec:10013` | The registry returned no DS record. Your local records were preserved rather than wiped. Check `GET /dnssec`. |
| `Dna.DomainService:Domain:10007` | Domain not found or not yours. |
| `Dna.DomainService:Domain:10070` | Waiting for registry documents. |
| `Dna.DomainService:Epp:10001` | The registry rejected the operation. |
