Managing DNS records

Tandem runs authoritative DNS (PowerDNS) for every zone your org owns — whether the domain was registered through Tandem or adopted via “Add external domain”. These five tools are the programmatic zone editor. For attaching a domain to a service (rather than editing raw records), see Custom domains and DNS and Domain lifecycle.

Finding a zone and its records

{ "name": "list_dns_records", "arguments": { "hostname": "example.com" } }

Pass the zone apex (example.com) or any name under it (api.example.com) — both resolve to the owning zone. list_dns_records is read-only (needs the read action) and returns:

  • the zone: { id, mode, status, defaultTtl, last sync info }
  • every record: { id, name, type, content, ttl, priority, managedBy, managedPurpose, takeoverable }

Records with managedBy='platform' back something Tandem runs for you — managedPurpose says what: web_default (the seeded apex/www A records), service_routing (a domain attached to a Tandem service), or email (MX/SPF/DKIM/DMARC). They can’t be edited or deleted directly, but web-serving ones (takeoverable=true) can be taken over (below).

Creating a record

{ "name": "create_dns_record",
  "arguments": { "hostname": "example.com", "name": "www", "type": "CNAME",
                 "content": "example.com" } }

Supported types: A, AAAA, CNAME, ALIAS, NS, TXT, MX, SRV, CAA. Field rules:

  • name is relative to the zone; use @ for the apex.
  • Put the value in content — an IP for A/AAAA, a hostname for CNAME/ALIAS/NS/MX/SRV, text for TXT, the value for CAA.
  • Structured fields go in their own params: priority (MX/SRV), weight + port (SRV), flags + tag (CAA). ttl is optional.
  • CNAME is rejected at the apex (use ALIAS) and cannot coexist with any other record at the same name.

The record is written and pushed to the authoritative nameservers immediately; resolver propagation still takes up to the record’s TTL. If PowerDNS is momentarily unavailable the record is saved and the response returns syncPending=true — the reconcile loop converges within minutes, no retry needed.

Updating a record

{ "name": "update_dns_record",
  "arguments": { "hostname": "example.com", "recordId": "…", "name": "www",
                 "type": "CNAME", "content": "new-target.example.com" } }

Get the recordId from list_dns_records. Supply the full desired record — this replaces the record’s values, it is not a partial patch. Editing name or type re-syncs both the old and new RRset so nothing drifts.

Deleting a record

{ "name": "delete_dns_record", "arguments": { "hostname": "example.com", "recordId": "…" } }

Irreversible — recreate with create_dns_record if removed by mistake. Resolvers may keep serving the old answer until the TTL expires.

Taking over a Tandem-managed record

{ "name": "take_over_dns_record", "arguments": { "hostname": "example.com", "recordId": "…" } }

Use this when the org runs its website on its own infrastructure and uses Tandem as the nameserver (and optionally the email host). Tandem seeds apex/www A records — and writes one per attached service domain — pointing at Tandem. Taking such a record over flips it to managedBy='user': Tandem stops managing, restoring, or cascade-deleting it, and update_dns_record / delete_dns_record work on it. Point it at any server with a normal update afterwards — published DNS doesn’t change until you edit it.

Notes:

  • Only web-serving records are takeoverable (takeoverable=true in list_dns_records). Email records return record_not_takeoverable — enable/disable email for the domain instead.
  • You can’t sidestep takeover by adding a second record at the same name/type: that returns record_managed_by_platform (both values would publish side by side).
  • Idempotent — re-running it on a record you already own is a no-op.
  • If the record was routing an attached service’s hostname, that hostname only reaches the service while the record points at Tandem.

Permissions

list_dns_records needs the read action. create_/update_/delete_/take_over_dns_record all need the manage action and developer (or higher) role on the owning org. Platform-managed records return record_managed_by_platform if you try to change them without taking them over first.