Custom Domain docs
DNS

DNS providers

The built-in auto-write fleet, the connect rails, and the parity census.

The Connect engine ships 38 built-in DNS provider adapters plus a full parity census of ~63 target providers, each routed to its best working mode (dc / oauth / api / manual). When a customer's domain is at a supported provider, records can be written automatically through one of the connect rails; otherwise the flow falls back to guided-manual.

By default no provider credential is stored. Rail C (OAuth) uses a one-time token that is always discarded, and Rail A (provider-hosted one-click setup) uses no server-to-server credential at all. A Rail B (API-token) credential is likewise used once and discarded unless the user opts in to remembering it, in which case it is stored encrypted at rest and revocable any time.

Auto-write fleet (a sample)

These providers have a libdns-shaped writer registered in the adapter registry. Each is idempotent and reconciles full record sets (multi-value rrsets are preserved, stale records removed). The live, complete set is returned by GET /v1/providers, and GET /v1/providers/census routes every target provider to its working mode.

ProviderKeyCredential (used once; stored only if you opt in)
CloudflarecloudflareScoped API token (Bearer)
DigitalOceandigitaloceanAPI token (Bearer)
GandigandiLiveDNS API key
deSECdesecAPI token
HetznerhetznerDNS API token
VercelvercelAPI token (Bearer)
DNSimplednsimpleAPI token (Bearer)
Porkbunporkbunapikey:secretapikey pair
LinodelinodePersonal access token (Bearer)
VultrvultrAPI key (Bearer)
Name.comnamecomuser:token (Basic)
GoDaddygodaddygd_pat_… (Bearer); legacy key:secret (sso-key) still accepted
IONOSionosAPI key (prefix.secret)
NetlifynetlifyAPI token (Bearer)
Amazon Route 53route53accessKeyId:secretAccessKey[:sessionToken] (SigV4); zone arg = Hosted Zone ID
Google Cloud DNSgcpdnsService-account JSON (+ managed_zone); OAuth2 JWT → bearer
Azure DNSazurednsJSON {tenant_id,client_id,client_secret,subscription_id,resource_group}
NamecheapnamecheapapiUser:apiKey[:userName[:clientIp]] (the calling IP must be whitelisted — 34.234.249.128 on the hosted service)
One-click setup (provider-hosted)domain-connectRedirect-based (no server token)

The registry also includes AliDNS, Bunny, ClouDNS, Constellix, DNSMadeEasy, DreamHost, Dynadot, EasyDNS, GleSYS, Hostinger, LuaDNS, NameSilo, Njalla, OVH, Openprovider, PowerDNS, Simply, Spaceship, TransIP, and WordPress.com.

Using a provider token (Rail B)

The BYO-token rail writes a connection's records through the customer's provider with a scoped token, used once and (by default) not persisted:

curl -X POST http://localhost:8080/v1/connections/<ID>/apply \
  -H "Authorization: Bearer <WIDGET_JWT>" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "cloudflare",
    "zone": "customer.com",
    "credential": { "token": "<scoped-provider-token>" },
    "records": [ { "type": "CNAME", "host": "app", "value": "edge.customdomain.ai" } ]
  }'

Scope the token to the customer's zone(s) with edit rights; it is used for the single write and then discarded, unless the user opts in to remembering it for reuse.

The colon-joined formats in the table above are the API wire format for credential.token. The console never asks anyone to type them: each part is a separate labeled field (GoDaddy: one Personal Access Token; Namecheap: username + API key; Route 53: access key ID + secret access key; Cloudflare: one token), linked to the provider's credential page with step-by-step instructions, and joined for you on submit. See Provider setup.

Apex (root) domains

A plain CNAME is illegal at a zone's apex (example.com, as opposed to app.example.com) per RFC 1034 — providers instead offer their own CNAME-like record for the root: a flattened CNAME, an ALIAS, or an ANAME. Customdomain hides that split behind one provider-agnostic record type internally and always shows it to you as ALIAS, whichever spelling the provider actually uses underneath:

Native spelling at the apexProviders
Flattened CNAMECloudflare, Netlify, EasyDNS, deSEC, Bunny, Vercel
ALIASDNSimple, Route 53, Namecheap, Porkbun, PowerDNS
ANAMEConstellix, DNSMadeEasy

If a customer's provider isn't in one of those three groups, it has no CNAME-family capability at the root at all, and connecting a bare apex domain there returns a distinct apex_not_supported error (422) instead of sending the provider a record type it would reject. The error message names three concrete alternatives: add A records manually at the provider, connect through a provider that does support ALIAS/ANAME, or connect www.<domain> instead of the bare apex. Subdomains (app.example.com) are unaffected either way — a plain CNAME is always legal there.

How connecting works — what you need per provider

There are three real-world requirement classes. "Code ✅" means the adapter is built and tested; the "External" column is what you or your customer must still do — the parts code can't remove.

ModelProvidersCodeExternal step
BYO API token (Rail B)Cloudflare, DigitalOcean, Gandi, deSEC, Hetzner, Vercel, DNSimple, Porkbun, Linode, Vultr, Name.com, GoDaddy, IONOS, NetlifyCustomer generates a scoped token and pastes it. No approval for most — Namecheap still requires enabling API access + IP-whitelisting + an account threshold. GoDaddy's legacy API keys were gated by account tier; its Personal Access Tokens are not, so a PAT is the way in for an ordinary account.
Machine credential (OAuth2 / signed) (Rail B)Google Cloud DNS, Azure DNS, Amazon Route 53Customer creates a service account (GCP) / app registration (Azure) / IAM key (AWS) and pastes the credential. No approval of your service; the customer needs cloud-admin access.
3-legged consumer OAuth (Rail C — "Log in with your provider, click Allow")per registered provider⚠️ business stepYou register an OAuth app with each provider and pass its partner/approval process (legal, branding, security review). The code path exists and is wired via OAUTH_<PROVIDER>_CLIENT_ID/_SECRET; provider-hosted one-click setup (Rail A) is the built-in alternative that avoids per-provider OAuth registration.

So: the built adapters are code-complete. Going live as a service means (a) some providers gate who can get API access, and (b) the frictionless "connect account" UX (Rail C) requires per-provider OAuth-app approval that only your company can obtain.

One-click setup (Rail A)

Rail A uses the provider-hosted one-click protocol and is redirect-based: the customer is sent to their provider, which applies a signed template and returns. There is no server-to-server token. Discovery is via TXT _domainconnect.<domain> → the provider's settings endpoint. It offers a smooth connect flow without registering a bespoke OAuth app per provider. Both the synchronous redirect and the asynchronous variant are supported (POST /v1/connections/{id}/domainconnect:start).

Roadmap

  • Squarespace — no general third-party DNS-record write API; connect via one-click setup / guided-manual instead of a token adapter.
  • Additional libdns providers can be added by implementing a Writer + conformance test. Per-provider credential steps live in Provider setup.

On this page