# Porkbun-style API

`https://api.maildns.io/api/json/v3` takes the same paths and JSON bodies as Porkbun's API v3 for DNS records. Point a Porkbun client at that base URL and give it your MailDNS key and secret.

## Requests and replies

Every call is a `POST` with a JSON body that carries the key and secret, plus the call's own fields:

```json
{"apikey": "KEY", "secretapikey": "SECRET"}
```

- `{domain}` in a path is one of your hosts (`advisor.example.com`) or the root domain they live in (`example.com`). See [Hosts](https://maildns.io/docs/hosts.md).
- In requests, `name` (and `{subdomain}` in paths) is the part in front of `{domain}`: `_dmarc`, `google._domainkey`. Leave it empty for `{domain}` itself. Replies give full names, as Porkbun does.
- Record ids are numeric strings.
- Success: `{"status": "SUCCESS", ...}`. Errors: `{"status": "ERROR", "message": "..."}` with a 4xx HTTP status (429 when you should wait and retry, 503 when the change can't be made right now). Check `status`; the `message` texts are listed in [Errors](https://maildns.io/docs/errors.md).

```json
{"status": "ERROR", "message": "This API key doesn't work for nameservers."}
```

## Endpoints

| Path (all `POST`) | Does |
|---|---|
| `/ping` | Check the key; returns the IP address you called from |
| `/domain/listAll` | Your hosts |
| `/domain/getNs/{domain}` | The nameservers (read-only) |
| `/domain/updateNs/{domain}` | Refused: nameservers |
| `/dns/retrieve/{domain}` | All records |
| `/dns/retrieve/{domain}/{id}` | One record |
| `/dns/retrieveByNameType/{domain}/{type}[/{subdomain}]` | Records of one type at one name |
| `/dns/create/{domain}` | Create a record; returns its `id` |
| `/dns/edit/{domain}/{id}` | Change one record |
| `/dns/editByNameType/{domain}/{type}[/{subdomain}]` | Change every record of that type at that name |
| `/dns/delete/{domain}/{id}` | Delete one record |
| `/dns/deleteByNameType/{domain}/{type}[/{subdomain}]` | Delete every record of that type at that name |

## Check the key

```bash
curl https://api.maildns.io/api/json/v3/ping \
  -H 'Content-Type: application/json' \
  -d '{"apikey": "KEY", "secretapikey": "SECRET"}'
```

```json
{"status": "SUCCESS", "yourIp": "203.0.113.7"}
```

## List hosts

```bash
curl https://api.maildns.io/api/json/v3/domain/listAll \
  -H 'Content-Type: application/json' \
  -d '{"apikey": "KEY", "secretapikey": "SECRET"}'
```

```json
{
  "status": "SUCCESS",
  "domains": [
    {
      "domain": "advisor.example.com",
      "status": "ACTIVE",
      "tld": "com",
      "createDate": "2025-01-01 00:00:00",
      "expireDate": "2027-01-01 00:00:00",
      "securityLock": "1",
      "whoisPrivacy": "1",
      "autoRenew": 1,
      "notLocal": 0
    }
  ]
}
```

The dates describe the root domain. `/domain/getNs/advisor.example.com` replies `{"status": "SUCCESS", "ns": ["ns1.example.net", "ns2.example.net"]}`.

## Read records

```bash
curl https://api.maildns.io/api/json/v3/dns/retrieve/advisor.example.com \
  -H 'Content-Type: application/json' \
  -d '{"apikey": "KEY", "secretapikey": "SECRET"}'
```

```json
{
  "status": "SUCCESS",
  "records": [
    {"id": "106926652", "name": "advisor.example.com", "type": "MX", "content": "smtp.google.com", "ttl": "3600", "prio": "1", "notes": ""},
    {"id": "106926653", "name": "advisor.example.com", "type": "TXT", "content": "v=spf1 include:_spf.google.com ~all", "ttl": "3600", "prio": "0", "notes": ""},
    {"id": "106926654", "name": "_dmarc.advisor.example.com", "type": "TXT", "content": "v=DMARC1; p=quarantine", "ttl": "3600", "prio": "0", "notes": ""}
  ]
}
```

One type at one name: `/dns/retrieveByNameType/advisor.example.com/TXT/_dmarc`. For `{domain}` itself, leave the subdomain off: `/dns/retrieveByNameType/advisor.example.com/TXT`.

## Create a record

```bash
curl https://api.maildns.io/api/json/v3/dns/create/advisor.example.com \
  -H 'Content-Type: application/json' \
  -d '{"apikey": "KEY", "secretapikey": "SECRET",
       "name": "", "type": "TXT", "ttl": "3600",
       "content": "google-site-verification=3J9tq0iV8pTt1yW5zKc2mE7aR4bN6hD0sF1gL8xQ"}'
```

```json
{"status": "SUCCESS", "id": "106926659"}
```

| Field | Value |
|---|---|
| `name` | The part in front of `{domain}` (`_dmarc`, `google._domainkey`), or empty for `{domain}` itself |
| `type` | `A`, `AAAA`, `CNAME`, `MX` or `TXT` |
| `content` | The value. TXT without surrounding quotes, in one piece. |
| `ttl` | Optional; see [the TTL rule](https://maildns.io/docs/keys.md#record-rules) |
| `prio` | MX priority, 0 to 65535 |

If the same record (type, name, content) is already there, you get its `id` back instead of a second copy, so retrying a create is harmless.

## Change records

By id, sending the whole record:

```bash
curl https://api.maildns.io/api/json/v3/dns/edit/advisor.example.com/106926654 \
  -H 'Content-Type: application/json' \
  -d '{"apikey": "KEY", "secretapikey": "SECRET",
       "name": "_dmarc", "type": "TXT", "content": "v=DMARC1; p=reject"}'
```

Or every record of a type at a name, sending only the new values:

```bash
curl https://api.maildns.io/api/json/v3/dns/editByNameType/advisor.example.com/TXT/_dmarc \
  -H 'Content-Type: application/json' \
  -d '{"apikey": "KEY", "secretapikey": "SECRET", "content": "v=DMARC1; p=reject"}'
```

Record ids come from the record itself, so an edited record gets a new id. Retrieve the records again before using an id after an edit.

## Delete records

```bash
curl https://api.maildns.io/api/json/v3/dns/delete/advisor.example.com/106926654 \
  -H 'Content-Type: application/json' \
  -d '{"apikey": "KEY", "secretapikey": "SECRET"}'
```

`/dns/deleteByNameType/advisor.example.com/TXT/_dmarc` deletes every TXT record at `_dmarc.advisor.example.com`.

## Nameservers

`/domain/updateNs/{domain}`, and creating an NS record, reply `{"status": "ERROR", "message": "This API key doesn't work for nameservers."}`. Reading them with `/domain/getNs/{domain}` is fine.

## Check that a record is live

The live check is a [Dynadot-style call](https://maildns.io/docs/propagation.md), and it takes the same key:

```bash
curl https://api.maildns.io/api3.json \
  --data-urlencode key=KEY --data-urlencode command=dns_status \
  --data-urlencode domain=advisor.example.com --data-urlencode subdomain=@ \
  --data-urlencode record_type=txt \
  --data-urlencode value=google-site-verification=3J9tq0iV8pTt1yW5zKc2mE7aR4bN6hD0sF1gL8xQ
```

Wait for `"Live": "yes"` before asking Google or Microsoft to verify.
