Let other services and AI agents create zones, manage records and check delegation. The REST API and the MCP server share one set of operations, one permission model and one audit log.
An admin creates a token on the API tokens page of the panel. Send it as a bearer token on every request:
curl https://api.dnsaman.com/api/v1/token \
-H "Authorization: Bearer dnsa_…"Each token has an owner and a set of permissions:
| Permission | Allows |
|---|---|
dns.read | List zones, read records, check delegation. |
dns.write | Create, update and delete records in reachable zones. |
zone.create | Create zones. A non-admin owner is granted the zone it creates. |
zone.delete | Delete reachable zones. Irreversible. |
user (token name) with the caller's IP. Record contents are left out of the log.Base URL https://api.dnsaman.com/api/v1. Requests and responses are JSON. Successful responses look like {"success": true, "result": …}, and failures like {"success": false, "errors": [{"message": "…"}]}.
| Method | Path | Permission | Notes |
|---|---|---|---|
| GET | /api/v1/token | any | Who this token is and what it may do |
| GET | /api/v1/zones | dns.read | Zones this token can reach |
| POST | /api/v1/zones | zone.create | Body: {"name", "hostmaster"?} |
| GET | /api/v1/zones/{zone} | dns.read | Zone, nameservers and all records |
| DELETE | /api/v1/zones/{zone} | zone.delete | Zone and every record in it |
| GET | /api/v1/zones/{zone}/records | dns.read | Query: ?name=&type= |
| POST | /api/v1/zones/{zone}/records | dns.write | Body: {"name", "type", "content", "ttl"?, "proxied"?} |
| PATCH | /api/v1/zones/{zone}/records/{id} | dns.write | Any subset of the record fields |
| DELETE | /api/v1/zones/{zone}/records/{id} | dns.write | |
| GET | /api/v1/zones/{zone}/delegation | dns.read | Is the domain pointed at us, and is every node current |
# Create a record
curl -X POST https://api.dnsaman.com/api/v1/zones/example.com/records \
-H "Authorization: Bearer $DNSAMAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "mail", "type": "A", "content": "203.0.113.25", "ttl": 3600}'
# → 201
{"success": true, "result": {"id": 42, "name": "mail", "type": "A", "ttl": 3600,
"content": "203.0.113.25", "proxied": false}}
# Change just the TTL
curl -X PATCH https://api.dnsaman.com/api/v1/zones/example.com/records/42 \
-H "Authorization: Bearer $DNSAMAN_TOKEN" \
-H "Content-Type: application/json" -d '{"ttl": 300}'Every change is published to all nameservers before the response returns. Creating a record that already exists with the same name, type and content returns 400, so a retried request does not create a second copy.
| Field | Format |
|---|---|
name | "@" for the apex, a relative label such as "www" or "_dmarc", or a full name inside the zone. "*" and "*.sub" are wildcards. |
type | A, AAAA, CNAME, MX, TXT, NS, SRV, CAA, PTR |
content | RDATA as in a zone file: "10 mx.example.com" (MX), "0 issue \"letsencrypt.org\"" (CAA), "10 5 443 host.example.com" (SRV). Dotted hostnames are made absolute. |
ttl | 60 to 604800 seconds. Defaults to the zone TTL. |
proxied | A/AAAA only. Publishes the edge address instead of the origin. |
The MCP endpoint at https://api.dnsaman.com/api/mcp uses Streamable HTTP and is stateless: send JSON-RPC over POST with the same bearer token. Agents only see the tools their token has permission for.
Claude Code
claude mcp add --transport http dnsaman https://api.dnsaman.com/api/mcp \
--header "Authorization: Bearer dnsa_…"Any client with a JSON config (Claude Desktop via mcp-remote, Cursor, …)
{
"mcpServers": {
"dnsaman": {
"type": "http",
"url": "https://api.dnsaman.com/api/mcp",
"headers": { "Authorization": "Bearer dnsa_…" }
}
}
}| Tool | Permission | What it does |
|---|---|---|
verify_token | any | Show which account and permissions this API token carries. |
list_zones | dns.read | List the zones this token can reach. |
get_zone | dns.read | Get a zone, its authoritative nameservers and all of its records. |
create_zone | zone.create | Create a zone. Returns the nameservers the domain owner must set at their registrar. |
delete_zone | zone.delete | Delete a zone and every record in it. Irreversible. |
list_records | dns.read | List records in a zone, optionally filtered by name and/or type. |
create_record | dns.write | Add a record to a zone and publish it. Exact duplicates are refused. |
update_record | dns.write | Change a record. Fields left out keep their current value. |
delete_record | dns.write | Delete a record by id. |
check_delegation | dns.read | Check whether the domain is delegated to our nameservers and every node serves the current zone. |
Tool arguments are the same fields as the REST bodies, plus zone (and id for record operations).
The usual flow for a service like a mail provider adding a customer domain. It uses a token with dns.read dns.write zone.create owned by a dedicated user:
T="Authorization: Bearer $DNSAMAN_TOKEN"; J="Content-Type: application/json"
API=https://api.dnsaman.com/api/v1
# 1. Create the zone. result.nameservers is what the customer sets at their registrar.
curl -X POST $API/zones -H "$T" -H "$J" -d '{"name": "customer.com"}'
# 2. Add the mail records.
curl -X POST $API/zones/customer.com/records -H "$T" -H "$J" \
-d '{"name": "@", "type": "MX", "content": "10 mx1.mail.example.net"}'
curl -X POST $API/zones/customer.com/records -H "$T" -H "$J" \
-d '{"name": "@", "type": "TXT", "content": "v=spf1 include:_spf.mail.example.net -all"}'
curl -X POST $API/zones/customer.com/records -H "$T" -H "$J" \
-d '{"name": "rm1._domainkey", "type": "TXT", "content": "v=DKIM1; k=rsa; p=MIIB…"}'
curl -X POST $API/zones/customer.com/records -H "$T" -H "$J" \
-d '{"name": "_dmarc", "type": "TXT", "content": "v=DMARC1; p=quarantine"}'
# 3. Poll until the registrar change is live (results are cached for 30 s).
curl $API/zones/customer.com/delegation -H "$T"
# → result.delegated && result.ok once the internet sees our nameservers| Status | Meaning |
|---|---|
400 | Validation failed: bad record content, duplicate record, zone already exists |
401 | Missing, wrong, expired or revoked token, disabled owner, or an IP outside the allowlist |
403 | The token lacks the permission this operation needs |
404 | No such zone or record, or one this token may not see (same answer on purpose) |
413 | Body over 64 KiB |
429 | Too many failed authentications from this IP; wait out the lockout |
Over MCP, a failed tool call comes back as a normal result with isError: true and the same message, so the agent can read it and correct its request.