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

TypeWhat it does
AMap a name to an IPv4 address
AAAAMap a name to an IPv6 address
CNAMEAlias to another name
TXTFree-form text (SPF, DKIM, domain verification)
MXMail exchanger
NSDelegation to other nameservers (sub-zone)
SRVService records
CAACertificate Authority authorisation
SOAStart of Authority — managed for you
PTRReverse mapping
Weighted A / AAAA / CNAMESame 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.