Records
Records are written into a zone. Every create/update/delete carries a type field — the API uses that as a discriminator to interpret the rest of the payload.
A record’s composite ID is <zone_name>:<type>:<name>. That’s what you pass to update/delete and what’s returned in list.
Supported types
| Type | What it does |
|---|---|
A | Map a name to an IPv4 address |
AAAA | Map a name to an IPv6 address |
CNAME | Alias to another name |
TXT | Free-form text (SPF, DKIM, domain verification) |
MX | Mail exchanger |
NS | Delegation to other nameservers (sub-zone) |
SRV | Service records |
CAA | Certificate Authority authorisation |
SOA | Start of Authority — managed for you |
PTR | Reverse mapping |
| Weighted A / AAAA / CNAME | Same as the base type but with per-target weights for round-robin |
A and AAAA
POST /dns/record/create
Authorization: Bearer <token>
Content-Type: application/json
{
"zone_name": "example.com",
"type": "A",
"name": "api",
"ttl": 300,
"records": ["203.0.113.10", "203.0.113.11"]
}Multiple values become round-robin answers. Replace A with AAAA for IPv6.
CNAME
{
"zone_name": "example.com",
"type": "CNAME",
"name": "www",
"ttl": 300,
"records": ["example.com."]
}A CNAME can’t coexist with any other record at the same name (DNS rule, not Excloud’s). Don’t put CNAMEs at the zone apex (@) — use A/AAAA there.
TXT
{
"zone_name": "example.com",
"type": "TXT",
"name": "@",
"ttl": 3600,
"records": ["v=spf1 include:_spf.google.com ~all"]
}Use this for SPF, DKIM, DMARC, and ACME challenges. Excloud will quote the value automatically; don’t add literal quotes.
MX
{
"zone_name": "example.com",
"type": "MX",
"name": "@",
"ttl": 3600,
"records": [
{ "priority": 10, "target": "mx1.example.com." },
{ "priority": 20, "target": "mx2.example.com." }
]
}Lower priority wins.
SRV
{
"zone_name": "example.com",
"type": "SRV",
"name": "_sip._tcp",
"ttl": 300,
"records": [
{ "priority": 10, "weight": 60, "port": 5060, "target": "sip1.example.com." }
]
}CAA
{
"zone_name": "example.com",
"type": "CAA",
"name": "@",
"ttl": 3600,
"records": [
{ "flags": 0, "tag": "issue", "value": "letsencrypt.org" }
]
}NS (sub-zone delegation)
{
"zone_name": "example.com",
"type": "NS",
"name": "internal",
"ttl": 3600,
"records": ["ns1.internal.example.com.", "ns2.internal.example.com."]
}Delegates internal.example.com to a different nameserver set.
Weighted variants
A, AAAA, and CNAME each have a weighted variant whose records entries carry weights:
{
"zone_name": "example.com",
"type": "A_WEIGHTED",
"name": "shard",
"ttl": 60,
"records": [
{ "value": "203.0.113.10", "weight": 70 },
{ "value": "203.0.113.20", "weight": 30 }
]
}70% of resolvers get the first IP, 30% get the second. Useful for canary deploys; keep TTLs short while you’re shifting weights.
TTLs
There’s no enforced minimum but very small TTLs (< 60s) waste cache capacity and slow your clients. Use:
- 300–3600 for normal records
- 60–120 when you’re about to flip an A record over
- 3600+ for SPF/DKIM/CAA that rarely change
Update / delete
POST /dns/record/update
{ "zone_name": "example.com", "type": "A", "name": "api", ... }
POST /dns/record/delete
{ "zone_name": "example.com", "type": "A", "name": "api" }Update is a full replacement of the record set — pass every value you want to keep.
List
POST /dns/record/list
{ "zone_name": "example.com" }Returns every record in the zone (no pagination — zones are small).
Required permissions
dns:record:create/update/delete/list
Resource scoping is per-zone: exc:dns:zone/<zone-name>. See the
Policies guide.