# DNS records

The site's own DNS records (A, AAAA, CNAME, TXT, MX, SRV, CAA, NS).

## Nameserver status

`GET /sites/{site_id}/nameservers` · scope `dns:read`

The nameservers the domain must use (required), what it uses now (current, checked daily), whether it is pointed, whether the DNS zone expired at the provider (zone_expired: re-create it from the site page), and the registrar autopilot status when a registrar connection sets them for you.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/sites/123/nameservers" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/nameservers", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/nameservers", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "current": [
      "ada.ns.cloudflare.com",
      "bob.ns.cloudflare.com"
    ],
    "required": [
      "ada.ns.cloudflare.com",
      "bob.ns.cloudflare.com"
    ],
    "pointed": true,
    "main_cloud": false,
    "zone_expired": false,
    "dns_provider": "Cloudflare",
    "checked_at": "2026-09-19T13:25:00Z",
    "autopilot": null
  }
}
```

Errors: `not_found`, `rate_limited`, `scope_missing`, `unauthorized`

## List DNS records

`GET /sites/{site_id}/dns` · scope `dns:read`

The site's own DNS records (the DNS records tab), and the publishing state: the DNS provider, when the records were last published and any record the provider refused (with its error). Records the platform manages for the CDN are not listed and are never changed through this API.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |

### Example

```bash
curl -s "https://app.pbn.ltd/api/v1/sites/123/dns" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.get("https://app.pbn.ltd/api/v1/sites/123/dns", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/dns", {
  method: "GET",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "records": [
      {
        "id": 5501,
        "type": "MX",
        "name": "@",
        "value": "10 mail.example.com",
        "proxied": false,
        "updated_at": "2026-09-19T10:00:00Z",
        "parts": {
          "priority": 10,
          "target": "mail.example.com"
        }
      }
    ],
    "publishing": {
      "provider": "Cloudflare",
      "errors": [],
      "last_synced_at": "2026-09-19T10:01:00Z"
    }
  }
}
```

Errors: `not_found`, `rate_limited`, `scope_missing`, `unauthorized`

## Add a DNS record

`POST /sites/{site_id}/dns` · scope `dns:write`

Adds a record with the panel's own validation. It is published to the site's DNS provider within a minute or two (follow the job); your records are never overwritten by the platform.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |
| `type` | body | string (one of: A, AAAA, CNAME, TXT, MX, SRV, CAA, NS) | yes | Record type. |
| `name` | body | string | yes | "@" for the domain itself, or a name like blog, shop.eu, _dmarc, _sip._tcp. |
| `value` | body | string | yes | IPv4 (A), IPv6 (AAAA), host name (CNAME, MX, SRV target, NS), text (TXT) or CA domain (CAA). |
| `priority` | body | integer | no | MX and SRV. |
| `weight` | body | integer | no | SRV. |
| `port` | body | integer | no | SRV. |
| `caa_flags` | body | integer (one of: 0, 128) | no | CAA: 0 or 128 (critical). |
| `caa_tag` | body | string (one of: issue, issuewild, iodef) | no | CAA tag. |
| `proxied` | body | boolean | no | Cloudflare only, A/AAAA/CNAME: proxy through the CDN (off = DNS only). |

### Example

```bash
curl -s -X POST "https://app.pbn.ltd/api/v1/sites/123/dns" \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type": "TXT", "name": "@", "value": "google-site-verification=abc123"}'
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.post("https://app.pbn.ltd/api/v1/sites/123/dns", headers=headers, json={"type": "TXT", "name": "@", "value": "google-site-verification=abc123"}, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/dns", {
  method: "POST",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
  body: JSON.stringify({"type": "TXT", "name": "@", "value": "google-site-verification=abc123"})
});
console.log(res.status, await res.json());
```

### Response

`201`

```json
{
  "data": {
    "id": 5501,
    "type": "MX",
    "name": "@",
    "value": "10 mail.example.com",
    "proxied": false,
    "updated_at": "2026-09-19T10:00:00Z",
    "parts": {
      "priority": 10,
      "target": "mail.example.com"
    }
  }
}
```

Errors: `conflict`, `not_found`, `rate_limited`, `scope_missing`, `unauthorized`, `validation_failed`

## Change a DNS record

`PATCH /sites/{site_id}/dns/{record_id}` · scope `dns:write`

Changes a record; send only the fields that change.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |
| `record_id` | path | integer | yes | The DNS record id. |
| `type` | body | string (one of: A, AAAA, CNAME, TXT, MX, SRV, CAA, NS) | no | Record type. |
| `name` | body | string | no | "@" for the domain itself, or a name like blog, shop.eu, _dmarc, _sip._tcp. |
| `value` | body | string | no | IPv4 (A), IPv6 (AAAA), host name (CNAME, MX, SRV target, NS), text (TXT) or CA domain (CAA). |
| `priority` | body | integer | no | MX and SRV. |
| `weight` | body | integer | no | SRV. |
| `port` | body | integer | no | SRV. |
| `caa_flags` | body | integer (one of: 0, 128) | no | CAA: 0 or 128 (critical). |
| `caa_tag` | body | string (one of: issue, issuewild, iodef) | no | CAA tag. |
| `proxied` | body | boolean | no | Cloudflare only, A/AAAA/CNAME: proxy through the CDN (off = DNS only). |

### Example

```bash
curl -s -X PATCH "https://app.pbn.ltd/api/v1/sites/123/dns/5501" \
  -H "Authorization: Bearer $PBN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "google-site-verification=xyz789"}'
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.patch("https://app.pbn.ltd/api/v1/sites/123/dns/5501", headers=headers, json={"value": "google-site-verification=xyz789"}, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/dns/5501", {
  method: "PATCH",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`, "Content-Type": "application/json"},
  body: JSON.stringify({"value": "google-site-verification=xyz789"})
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "id": 5501,
    "type": "MX",
    "name": "@",
    "value": "10 mail.example.com",
    "proxied": false,
    "updated_at": "2026-09-19T10:00:00Z",
    "parts": {
      "priority": 10,
      "target": "mail.example.com"
    }
  }
}
```

Errors: `not_found`, `rate_limited`, `scope_missing`, `unauthorized`, `validation_failed`

## Delete a DNS record

`DELETE /sites/{site_id}/dns/{record_id}` · scope `dns:write`

Deletes a record; it is removed at the DNS provider within a minute or two.

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `site_id` | path | integer | yes | The site id (see GET /sites). |
| `record_id` | path | integer | yes | The DNS record id. |

### Example

```bash
curl -s -X DELETE "https://app.pbn.ltd/api/v1/sites/123/dns/5501" \
  -H "Authorization: Bearer $PBN_API_KEY"
```

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["PBN_API_KEY"]}
r = requests.delete("https://app.pbn.ltd/api/v1/sites/123/dns/5501", headers=headers, timeout=120)
print(r.status_code, r.json())
```

```javascript
const res = await fetch("https://app.pbn.ltd/api/v1/sites/123/dns/5501", {
  method: "DELETE",
  headers: {Authorization: `Bearer ${process.env.PBN_API_KEY}`}
});
console.log(res.status, await res.json());
```

### Response

`200`

```json
{
  "data": {
    "deleted": true,
    "id": 5501
  }
}
```

Errors: `not_found`, `rate_limited`, `scope_missing`, `unauthorized`
