# Zone versions

> Every DNS change creates a new zone version with author, reason and diff. Simulate first, apply against the expected version and roll back.

A zone's configuration (records, edge settings, rules and client) has a **version** number. Every change creates the
next version, with its author, its reason and the diff against the previous one. Versions are how changes reach the
edge and how you undo them.

This page covers what matters for DNS; [Change control](https://developers.openwaf.dev/change-control/) covers history, diffs, propagation and
rollback in depth.

## One change, one version

- Creating, updating or deleting one record is one version.
- A [batch](https://developers.openwaf.dev/dns/batch-changes/) (up to 500 operations), a bulk proxy switch in the dashboard, a BIND import or the email
  wizard is **one** version, applied all or nothing.
- Adding a zone creates version 1; the records imported from the public DNS right after are version 2.
- Deleting a zone leaves a last version in its history, which stays readable by id.

## Every change needs a reason

Each change carries a reason of 1 to 500 characters, kept in the version and in the audit log.

| Interface | How |
|---|---|
| Dashboard | Filled in for you from the change (for example “Add A record www”) |
| API | `X-Change-Reason` header. Missing: `422 reason_required` |
| CLI | `--reason "…"` |
| MCP | `reason` argument |

## Simulate, then apply

Any change can be simulated first: same validation, nothing applied. The simulation returns the versions
(`from_version`, `to_version`), the `diff` and any `warnings`.

```sh
# 1. Simulate
curl -X POST "https://dash.openwaf.dev/v1/orgs/$ORG/zones/$ZONE/records?dry_run=1" \
  -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", "proxied": true}'

# 2. Apply, only if the zone is still at the version you simulated against
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" -H 'If-Match: "41"' \
  -d '{"type": "A", "name": "www", "content": "203.0.113.10", "proxied": true}'
```

- A successful change answers with the new version in `X-Config-Version` and `ETag`.
- With `If-Match`, the change is refused with `412 version_conflict` (and the `current_version`) if anyone changed the
  zone in between: simulate again and review the new diff.
- The CLI simulates, shows the diff and asks for confirmation before every change (`--dry-run` stops after the
  simulation, `--yes` skips the question). MCP write tools simulate with `confirm: false` and apply with
  `confirm: true` and `expected_version`.

## History and rollback

The domain's **Changes › History** lists every version with its diff. Reverting to an earlier version creates a new
version with that configuration, records included, so the revert is itself in the history.

```sh
openwaf-cloud ctl zones history example.com
openwaf-cloud ctl zones diff example.com 40 41
openwaf-cloud ctl zones revert example.com 40 --reason "Undo the www change"
```

See [Change control](https://developers.openwaf.dev/change-control/) for propagation to the edge and the details of reverting.
