# Manage DNS records

> Create, edit and delete DNS records from the dashboard, the CLI, the API or an AI assistant, and import or export whole zones.

Records are managed per zone. Reading them needs the `readonly` role; changing them needs `tech` or higher. Every
change is a [new version](https://developers.openwaf.dev/dns/zone-versions/) with a reason.

Record names can be written as `@` (the apex), relative (`www`) or fully qualified with a final dot
(`www.example.com.`). Names and targets are stored in lower case without the final dot. See
[Record types and validation](https://developers.openwaf.dev/dns/record-types/) for the content of each type.

## List records

**Dashboard**

Open the domain's **DNS › Records**. Search, filter by name, type, content, proxy, TTL or comment, and choose the columns.
**CLI**

```sh
openwaf-cloud ctl records list example.com
```
**API**

```sh
curl "https://dash.openwaf.dev/v1/orgs/$ORG/zones/$ZONE/records" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```
Each item has `id`, `type`, `name`, `content`, `ttl`, `priority` (MX and SRV), `proxied`, `comment`, `tags`,
`created_at` and `updated_at`.
**MCP**

`list_records` with `zone: "example.com"`. The ids it returns are the ones `update_record`, `delete_record` and
`apply_records_batch` use.


## Create a record

**Dashboard**

Select **Add record**, choose the type and fill in name, content, TTL and proxy status. Optional **Comment** and
**Tags** are for your reference and never published. The preview sentence shows what the record will do.
**CLI**

```sh
openwaf-cloud ctl records add example.com type=A name=www content=203.0.113.10 proxied=true --reason "Add www"
openwaf-cloud ctl records add example.com type=MX name=@ content=mx1.example.net priority=10 --reason "Mail"
openwaf-cloud ctl records add example.com type=TXT name=@ content="v=spf1 mx -all" comment="SPF" --reason "SPF"
```
**API**

```sh
curl -X POST "https://dash.openwaf.dev/v1/orgs/$ORG/zones/$ZONE/records" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Change-Reason: Add www" \
  -d '{"type": "A", "name": "www", "content": "203.0.113.10", "ttl": 3600, "proxied": true,
       "comment": "web server", "tags": ["prod"]}'
```
Answers `201` with the record and the new version in `X-Config-Version`.
**MCP**

`create_record` with `zone`, `type`, `name`, `content` and optionally `ttl`, `priority`, `proxied`, `comment` and
`tags`, plus `reason`. The assistant simulates first (`confirm: false`), shows you the diff and applies with
`confirm: true` and `expected_version`.


## Edit a record

Only the fields you send change. Switching `proxied` on one `A`, `AAAA` or `CNAME` record switches every one at the
same name.

**Dashboard**

Select **Edit** in the record's row, change the fields and save. The proxy switch in the row toggles proxied and DNS
only directly.
**CLI**

The record is named by its id or by `name/TYPE`:
```sh
openwaf-cloud ctl records set example.com www/A content=203.0.113.20 --reason "New web server"
```
**API**

```sh
curl -X PATCH "https://dash.openwaf.dev/v1/orgs/$ORG/zones/$ZONE/records/$RECORD" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Change-Reason: New web server" \
  -d '{"content": "203.0.113.20"}'
```
Send `"priority": null` to clear a priority.
**MCP**

`update_record` with `zone`, `record` (the id from `list_records`) and the fields that change.


## Delete a record

A deleted record stops resolving once the change propagates. Deleting a proxied record stops serving its site.

**Dashboard**

Select the record, then **Delete**. Select several records to delete them in one change.
**CLI**

```sh
openwaf-cloud ctl records rm example.com old/CNAME --reason "Unused"
```
**API**

```sh
curl -X DELETE "https://dash.openwaf.dev/v1/orgs/$ORG/zones/$ZONE/records/$RECORD" \
  -H "Authorization: Bearer $OPENWAF_TOKEN" \
  -H "X-Change-Reason: Unused"
```
Answers `204`.
**MCP**

`delete_record` with `zone` and `record`.


## Import records

In delegated zones you can bring records in from three sources. All of them let you review the list and apply the
selected records as **one** version.

- **Current public DNS.** When a zone is added, OpenWAF copies the records it finds (and names seen in public
  certificate logs) without proxy. **Scan again** in the dashboard, or `GET /v1/orgs/{org}/zones/{zone}/import`, lists
  what is not in the zone yet.
- **BIND zone file** (up to 1 MiB) exported from your previous provider: **Import** in **DNS › Records**, or the CLI.
  OpenWAF shows what it leaves out and why (for example `SOA`, apex `NS` or unsupported types).

  ```sh
  openwaf-cloud ctl records import example.com zone.txt --reason "Import from old provider"
  ```

  Add `--proxied` to import `A`, `AAAA` and `CNAME` records as proxied. In the API,
  `POST /v1/orgs/{org}/zones/{zone}/import/bind` parses the file into candidates without applying anything; apply the
  ones you keep with a [batch](https://developers.openwaf.dev/dns/batch-changes/).
- **Many records from a script**: send them in one [batch](https://developers.openwaf.dev/dns/batch-changes/).

## Export records

**Export** in **DNS › Records** downloads the zone as a BIND zone file or CSV; with records selected, **Export
selection** exports only those.

## Things to know

- The apex `NS` records and the `SOA` are managed by OpenWAF and cannot be edited. `NS` records below the apex delegate
  a subdomain to other nameservers.
- An unproxied `CNAME` at the apex is published as an alias: OpenWAF resolves the target and answers with its
  addresses (CNAME flattening), so `MX` and `TXT` records can live next to it.
- In an [external zone](https://developers.openwaf.dev/dns/external-zones/) only proxied `A`, `AAAA` and `CNAME` records can be added: the rest of the
  DNS lives at your provider.
- Records cannot be changed in a disabled zone (`409 conflict`).
