# nxdns API リファレンス (AI エージェント向け)

nxdns (https://record.nxix.jp) は権威 DNS のホスティングサービスです。この文書は、AI コーディングエージェント (Claude Code、Cursor、Copilot など) が
nxdns の API でドメインとレコードを操作し、他の DNS サービスから移行するために必要なことを 1 ファイルにまとめたものです。

- 最新版: https://record.nxix.jp/docs/api.md (ログイン不要)
- 人向けのページ: https://record.nxix.jp/docs/api
- ネームサーバー: `ns1.nxix.jp`, `ns2.nxix.jp`

## エージェントへの指示 (必ず守ること)

1. **トークンを表示・保存しない。** トークンは環境変数 `NXDNS_API_TOKEN` から読む。コード・ログ・コミット・チャットに値を書かない。
   `.env` を使う場合は `.gitignore` に入っていることを確認する。
2. **変更の前に現状を読む。** `GET /zones/{zone}/rrsets` で現在のレコードを取得し、差分を利用者に見せてから変更する。
3. **まとめて変更するときは `POST /zones/{zone}/rrsets` を使う。** 1 回の呼び出しが 1 つのバージョンになり、1 件でも不正なら何も変わらない。
   先に `"dry_run": true` で検証し、保存される形を確認する。
4. **削除は慎重に。** `DELETE /zones/{zone}` はゾーンとすべてのレコードを消す。利用者の明示的な指示なしに実行しない。
5. **ネームサーバーの切り替え (レジストラでの NS 変更) と DS の登録・削除は API ではできない。** 利用者に手順を案内する。
6. 429 (レート制限) と 409 の `contention` は少し待って再試行する。それ以外の 4xx は再試行せず、メッセージを利用者に伝える。

## 認証

```
Authorization: Bearer $NXDNS_API_TOKEN
```

トークンはダッシュボードの「API トークン」(https://record.nxix.jp/account/tokens) で作る。`nxd_` で始まる 44 文字。
作成直後に `.env` ファイルとしてダウンロードできる (`NXDNS_API_TOKEN` と `NXDNS_API_URL` が入っている)。

| 権限 | できること |
|---|---|
| 編集 | すべての操作。ドメインを限定したトークンではゾーンの作成は不可 |
| 読み取り | GET のみ |
| 証明書の発行 (ACME) のみ | `/acme/present` と `/acme/cleanup` だけ |

組織 (チーム) のドメインは、ダッシュボードで組織に切り替えてから作ったトークンで扱う。

動作確認:

```sh
curl -s https://record.nxix.jp/api/v1/user/tokens/verify -H "Authorization: Bearer $NXDNS_API_TOKEN"
```

## 共通の形式

ベース URL: `https://record.nxix.jp/api/v1` 。JSON の形は Cloudflare API に準じる。

```json
{
  "success": true,
  "errors": [],
  "messages": [],
  "result": {},
  "result_info": {"page": 1, "per_page": 100, "count": 3, "total_count": 3, "total_pages": 1}
}
```

失敗時は `"success": false` と `"errors": [{"code": 10003, "message": "..."}]`。

| HTTP | code | 意味 | 再試行 |
|---|---|---|---|
| 400 | 10003 | 入力の検証エラー (理由はメッセージ) | しない |
| 401 | 10000 | トークンがない・無効・期限切れ | しない |
| 403 | 10001 | 権限不足、対象外のドメイン、システム管理のレコード (SOA、頂点の NS) | しない |
| 404 | 10002 | ゾーン・レコードがない | しない |
| 409 | 10006 | 重複・競合 (`contention` を含む場合は再試行可) | 場合による |
| 429 | 10004 | レート制限 (トークンごとに毎秒 4 件、バースト 200)。`Retry-After` あり | する |

一覧は `?page=` と `?per_page=` でページングする (ゾーンは 1 ページ最大 500 件)。

## 名前と値の書き方

- `name`: `@` (ドメインそのもの)、相対名 (`www`)、完全名 (`www.example.com`) のどれでもよい。
- TTL: 30〜604800 秒。省略時 300。
- TXT: 引用符なしで書けば自動で引用する (255 文字を超えると分割)。読み出すと `"v=spf1 -all"` のように引用符付きで返る。
- CNAME / MX / NS / SRV / ALIAS の宛先: ドットを含む名前 (`mail.example.net`) はそのまま完全名、1 ラベル (`mail`) はこのドメイン内の名前、
  `@` はドメインそのもの。保存時は末尾に `.` が付く (`mail.example.com.`)。
- MX は `"10 mail"`、SRV は `"10 5 443 target"` のように優先度を値に含める。
- 対応タイプ: A, AAAA, CNAME, ALIAS, MX, TXT, NS, SRV, CAA, HTTPS, SVCB, TLSA, SSHFP, PTR, DS, NAPTR, DNAME, CERT, LOC, SMIMEA, OPENPGPKEY, URI, HINFO, RP, AFSDB, KX, DHCID
- **ALIAS**: ドメインそのもの (`@`) を別のホスト名 (Vercel、Netlify、ロードバランサーなど) に向けるときに使う。CNAME は `@` に置けない。
- システムが管理するもの: SOA と、ドメイン頂点の NS。変更しようとすると 403。

## ゾーン

| メソッド | パス | 説明 |
|---|---|---|
| GET | `/zones` | 一覧 (`?name=example.com` で絞り込み) |
| POST | `/zones` | 作成 `{"name":"example.com"}` |
| GET | `/zones/{zone}` | 詳細 |
| DELETE | `/zones/{zone}` | 削除 (全レコードも消える) |
| GET | `/zones/{zone}/status` | 委任 (ネームサーバーの切り替え) の確認。`?force=1` で再確認 |

ゾーンの例:

```json
{"name": "example.com", "status": "pending", "records": 2, "dnssec": false, "serial": 2026110501,
 "name_servers": ["ns1.nxix.jp", "ns2.nxix.jp"], "modified_on": "2026-11-05T01:23:45Z"}
```

`status`: `active` (レジストリが nxdns に委任済み)、`pending` (まだ)、`unknown`。プランごとにドメイン数とレコード数の上限がある
(Free: 10 ドメイン、1 ドメイン 500 レコード)。

## RRset (推奨)

RRset は「1 つの名前とタイプのレコード一式」。

| メソッド | パス | 説明 |
|---|---|---|
| GET | `/zones/{zone}/rrsets` | 一覧 (`?name=` `?type=` で絞り込み) |
| PUT | `/zones/{zone}/rrsets/{name}/{type}` | 1 つの RRset を置き換え `{"ttl":300,"records":[...]}`。`records` が空なら削除 |
| POST | `/zones/{zone}/rrsets` | 最大 1,000 個の RRset を 1 回で置き換え (全部成功か、何も変わらないか) |

```sh
curl -s -X POST https://record.nxix.jp/api/v1/zones/example.com/rrsets \
  -H "Authorization: Bearer $NXDNS_API_TOKEN" -H "Content-Type: application/json" \
  -d '{"rrsets":[
        {"name":"@",   "type":"A",     "ttl":300, "records":["192.0.2.10"]},
        {"name":"www", "type":"CNAME", "ttl":300, "records":["@"]},
        {"name":"@",   "type":"MX",    "ttl":3600,"records":["10 mail.example.net"]},
        {"name":"@",   "type":"TXT",   "ttl":3600,"records":["v=spf1 include:_spf.example.net -all"]},
        {"name":"old", "type":"A",     "records":[]}
      ]}'
```

- 成功時: `{"version": 12, "serial": 2026110503, "changed": 5}`
- `"dry_run": true` を付けると何も変えずに検証し、保存される形を返す: `{"rrsets":[{"name":"www.example.com","type":"CNAME","ttl":300,"records":["example.com."]}]}`
- 一覧の要素: `{"name":"www.example.com","type":"A","ttl":300,"records":["192.0.2.10"],"locked":false}`。`locked: true` は変更できない (頂点の NS)。

## レコード (1 件ずつ)

| メソッド | パス | 説明 |
|---|---|---|
| GET | `/zones/{zone}/records` | 一覧 (`?type=` `?name=` `?content=`) |
| POST | `/zones/{zone}/records` | 作成 `{"name":"www","type":"A","content":"192.0.2.10","ttl":300}` |
| GET / PUT / PATCH / DELETE | `/zones/{zone}/records/{id}` | 取得・全項目更新・一部更新・削除 |

レコード: `{"id":"…","name":"www.example.com","type":"A","content":"192.0.2.10","ttl":300}`。MX / SRV / URI は `priority` も使える。
`id` は名前・タイプ・値から決まり、値を変えると新しい ID になる。

メモ: `comment` (200 文字まで、ダッシュボードのメモと共通)。作成時に `"comment":"…"` を付けられ、取得・一覧に含まれる。
メモだけを変えるときは `PATCH /zones/{zone}/records/{id}` に `{"comment":"…"}` だけを送る (ゾーンは再配信されない)。`""` で削除。
`PATCH /records/{id}` で値を変えてもメモは引き継がれ、レコードを削除するとメモも消える。
RRset API (`PUT /rrsets/{name}/{type}`、`POST /rrsets`) はメモを設定できないが、置き換えでは次のように扱う:
値が変わらないレコードのメモはそのまま。1 件を別の 1 件に置き換えたときはメモを引き継ぐ。
それ以外 (複数件を入れ替えた等) で消えたレコードのメモは削除される。複数件の rrset でメモを保ちたいときは、
変わらない値を残したまま追加・削除するか、`/records/{id}` で 1 件ずつ変更する。

## ゾーンファイル

| メソッド | パス | 説明 |
|---|---|---|
| GET | `/zones/{zone}/export` | BIND 形式 (text/plain) |
| POST | `/zones/{zone}/import` | BIND 形式の本文で**すべてのレコードを置き換える** (`Content-Type: text/plain`) |

取り込めなかった行は `result.skipped` に理由付きで返り、残りは取り込まれる。SOA と頂点の NS は無視される。

## DNSSEC

| メソッド | パス | 説明 |
|---|---|---|
| GET | `/zones/{zone}/dnssec` | `{"status":"active","ds":[{"key_tag":…,"algorithm":13,"digest_type":2,"digest":"…","record":"example.com. 3600 IN DS …"}]}` |
| PUT | `/zones/{zone}/dnssec` | `{"enabled": true}` / `{"enabled": false}` |

有効化したら、`ds` の値を利用者がレジストラに登録する。無効化の前に、利用者がレジストラから DS を削除する
(DS が残っていると拒否される)。

## 証明書の自動発行 (ACME DNS-01)

`POST /acme/present` と `POST /acme/cleanup` に `{"fqdn":"_acme-challenge.example.com.","value":"…"}`。
「ACME のみ」のトークンを使う。acme.sh 用プラグイン: `https://record.nxix.jp/dl/dns_nxdns.sh`。lego / Traefik / Caddy は
`httpreq` (`HTTPREQ_ENDPOINT=https://record.nxix.jp/api/v1/acme`、`HTTPREQ_USERNAME=nxdns`、`HTTPREQ_PASSWORD=<トークン>`)。

## Terraform

```hcl
terraform {
  required_providers {
    nxdns = { source = "record.nxix.jp/nxdns/nxdns" }
  }
}
provider "nxdns" {} # NXDNS_API_TOKEN を読む

resource "nxdns_zone" "main" { name = "example.com" }

resource "nxdns_rrset" "www" {
  zone    = nxdns_zone.main.name
  name    = "www"
  type    = "A"
  ttl     = 300
  records = ["192.0.2.10"]
}
```

既存のものは取り込んでから管理する: `terraform import nxdns_zone.main example.com`、
`terraform import nxdns_rrset.www example.com/www.example.com/A`。

## 他の DNS サービスから移行する手順

エージェントは次の順で進める。手順 5 と 6 は利用者自身がレジストラで行う。

1. **今のレコードを集める。**
   - Cloudflare: ダッシュボードの DNS →「エクスポート」、または API `GET https://api.cloudflare.com/client/v4/zones/{id}/dns_records/export` (BIND 形式)。
     「プロキシ (オレンジの雲)」のレコードは Cloudflare の IP を指しているので、元のサーバーの IP に書き換えるか、利用者に確認する。
   - Route 53: `aws route53 list-resource-record-sets --hosted-zone-id …` (JSON) を RRset に変換する。
   - お名前.com・ムームードメイン・Xserver・さくらなど: 管理画面の一覧を利用者に貼ってもらう。ゾーンファイルを出せる場合はそれを使う。
   - 取得できない場合: ダッシュボードの「ドメインを追加」は、今のネームサーバーに主な名前を問い合わせて自動で取り込める (https://record.nxix.jp/add)。
2. **ゾーンを作る。** `POST /zones {"name":"example.com"}`
3. **レコードを入れる。** BIND 形式なら `POST /zones/{zone}/import`、それ以外は `POST /zones/{zone}/rrsets` (先に `dry_run`)。
   移行元の NS・SOA は入れない (自動で管理される)。
4. **確認する。** 切り替え前でも nxdns のネームサーバーに直接問い合わせて確認できる:
   ```sh
   dig @ns1.nxix.jp example.com A +short
   dig @ns1.nxix.jp www.example.com A +short
   dig @ns1.nxix.jp example.com MX +short
   dig @ns1.nxix.jp example.com TXT +short
   ```
   移行元 (`dig @<今のネームサーバー> …`) と同じ答えになるか、名前ごとに比べて利用者に結果を見せる。メール (MX、SPF、DKIM、DMARC) は特に念入りに。
5. **DNSSEC を使っている場合**: 切り替えの前に、利用者がレジストラで DS を削除し、移行元で DNSSEC を無効にする。
   1〜2 日待ってから切り替える (残っていると、DNSSEC を検証するリゾルバーで名前解決できなくなる)。nxdns 側の DNSSEC は切り替え後に有効にする。
6. **ネームサーバーを切り替える。** 利用者がレジストラで NS を `ns1.nxix.jp` と `ns2.nxix.jp` に変更する。
7. **反映を確認する。** `GET /zones/{zone}/status?force=1` が `active` になるまで待つ (通常は数分〜数時間、最大 2 日)。
   移行元のゾーンは、`active` になってから最低 2 日は消さないよう利用者に伝える。

### 移行のコツ

- 切り替えの 1 日前に、移行元の TTL を 300 程度に下げておくと、問題があったときに戻しやすい。
- ゾーンファイルにある `$ORIGIN` と `$TTL` は解釈される。移行元固有の独自レコード (Cloudflare の「ページルール」など) は DNS ではないので移行対象外。
- 取り込み後は `GET /zones/{zone}/export` でゾーンファイルを出し、移行元と見比べられる。
