Changelog
How the product and API change over time.
Customdomain doesn't yet cut numbered releases — the hosted service is continuously updated, and the API is versioned separately (below).
API versioning policy
The public API is versioned by URI major (/v1) and is additive-only within a
version — new optional fields and endpoints can appear, but existing fields
never change meaning or disappear without a new major version. See the
API reference for the current surface.
Notable milestones
- Re-check is honest on live domains. A re-check on a domain connected
from the console or the widget verified an empty record set and reported
drift on a healthy domain; it now verifies the domain's actual records. The
connect flow also shows the right refusal for a hosting platform's
subdomain (
www.name.vercel.app) instead of "register it at your registrar", and GoDaddy's one-click status reads "hasn't onboarded our template" rather than "not offered". - Wix, Hover, Bluehost and friends are named. A domain at a manual-only host is identified from its nameservers and shown with that provider's DNS panel link, instead of "we couldn't detect your DNS host".
- Domain-purchase orders are listable and stale quotes expire.
GET /v1/sell/ordersreturns your application's orders; unpaid quotes older than a day becomeexpired(withpurchase.confirmation.expired). - "Unregistered" only when it's true. The pre-flight reports
registered: falseonly on an NXDOMAIN answer; a transient resolver failure omits the field and reportsdetection_error: resolverinstead. - Webhooks say why.
connection.failed(and any lifecycle event on a connection with a recorded diagnosis) now carrieserror_codeanderror_message. Connections also exposeretried_at. - Widget tells the host why it fell back. A new
customdomain:fallbackevent carries the reason (no sign-in or one-click at this provider, too many conflicting records, unsupported or unknown provider) and the message the user saw. - Widget recovers a failed connection in place. The records stay on
screen with the reason; I've added them — verify re-enters verification
(
:recheck). Re-sending an identical record set after a write returns 200. A stalled connection keeps its real diagnosis instead of a generic one. wwwworks with the root redirect. A root connection withwww_redirectnow serveswww.<root>too (the edge redirects the root there); previouslywwwwas refused at the edge and never got a certificate.- Rail truth on the pre-flight.
POST /v1/domains:checknow returnsrails(per rail: available, and one machine reason when not), the server'srecommended_rail,end_user_automatic, ablocked_reasonfor domains no rail can connect, adashboard_urlinto the provider's DNS panel, and the observednameservers. One-click setup is advertised only where the provider is measured to serve our template — never merely because it answered discovery — anddomainconnect:startrefuses what the check did not advertise, naming the provider. Hosting-platform subdomains (*.vercel.appand friends) are refused on create withplatform_subdomaininstead of waiting for DNS that can never appear. The console shows "What works at GoDaddy" from this block and links to the DNS panel; the widget explains why one-click isn't available and offers the panel link on the manual screen; the SDK exposes the same fields in camelCase. - Clearer setup, honest by provider. The connect flow now shows what your DNS provider can actually do before you choose a path, explains provider errors in plain words, never pretends one-click setup worked before you approved it, and offers "Check now" plus a fallback after a few minutes of waiting. A domain that's live but shows nothing tells you to set where it points. The overview explains the three ways to use Custom Domain (your own domain · your customers' domains via the widget · API/agents).
- Email-only domains are never served. Connections whose records are only mail (MX/SPF/DKIM) can't be approved at the edge; a domain pointed at us before it's active gets a clear page instead of a raw error.
- Late DNS no longer fails a connection. A
pendingconnection now waits as long as it takes (re-checked every six hours after the first three days) and connects the moment the records appear. If your domain already points at us when a connection had failed, real traffic re-enters it into verification automatically. Retrying a domain resumes the existing connection instead of creating a duplicate, and Finish setup now works on failed connections. - Honest pre-flight. The connect flow refuses domains that don't resolve
(unregistered / undelegated) and hosting-platform subdomains (
*.vercel.appand friends) with an explanation, instead of parking them on "waiting for DNS". - Root domains on GoDaddy / Namecheap / Name.com show the A records to add (those panels have no ALIAS type); the docs now say plainly which providers connect automatically and which are manual.
- Sign-up recovery. Check your email has a Resend the link button and says so honestly if sending fails; an expired verification link lands on a page that issues a new one.
- Route 53 accepts a session token for temporary (
ASIA…) credentials. - Clearer errors. A DNS-provider write that fails is reported as a 422
dns_write_failedwith the provider's reason — not a 503. - Tenant-scoped connection webhooks — connection lifecycle envelopes now
include the optional
user_idechoed from the connection'send_user_ref, allowing integrators to associate events with the originating tenant or end user. Connections created without anend_user_refcontinue to omit it. - Integrator-supplied record sets —
PUT /v1/connections/{id}/recordsreplaces a connection's desired records with a set the integrator authored, for records no template can derive (Amazon SES mints three Easy-DKIM CNAMEs per domain). Server-to-server only, and the one-click template rail refuses such a connection. See Create a connection. - Money-safe domain buying — embedded checkout that authorizes the card, registers the domain server-side (webhook-driven), and captures only after a successful registration; purchased domains land in the console pre-connected. See Buying a domain.
- Hosted MCP server live —
https://mcp.customdomain.ai/mcp(Streamable HTTP) with eleven agent tools, plus a console MCP server page under Integrate with credentials and client configs. - One-click setup template catalog live upstream — the full customdomain.ai template catalog (apex A/AAAA/CNAME/www, subdomain, www, wildcard, verify, connect-verified, CAA, and the email set) is merged into the provider-hosted one-click protocol's upstream template registry, and prod embeds the shipped catalog.
- One-click sync rail live at registry-mirroring DNS providers — apply URLs
are RS256-signed with the
_dck1selector (pubkey published at_dck1.customdomain.ai) and a conformance CI gate keeps the catalog valid. Providers that mirror the public template registry serve the catalog today (measured 2026-07-28: NameSilo and Glauca Digital serve all 18 templates, Domain Chief 16 of 18), and the rail is offered per-provider via theDC_SYNC_PROVIDERSallowlist. Providers that hand-curate their template sets (GoDaddy, IONOS, Cloudflare) require direct onboarding, which has not completed — for domains hosted there the widget offers the API-key and manual paths, plus one-click OAuth where the provider runs an OAuth rail (Cloudflare does; GoDaddy and IONOS do not). See Setup types & the ask gate. - Share links — forward a connect flow to a colleague with a link that carries
a 30-day expiry; expired share tokens return
404. - Frontend overhaul — the console gained a shared component library, dark mode,
a
⌘Kcommand palette, and a mobile-friendly layout. - Shop-style domain search — buying-domain search now groups results into the
exact name, other TLDs (
.com .net .org .io .co .ai .app .dev .xyz .me), and close name variants. See Buying a domain. - Opt-in credential reuse — you can optionally save a provider API key (encrypted at rest, per tenant + provider) to reuse it for other domains in one click, and revoke it any time under Settings → Connected provider accounts. Off by default; the plaintext never leaves the server. See Provider setup.
- Bulk connect — after providing a provider key, connect the other domains on that account (all or a subset) in one step, with per-domain status. Supported for Cloudflare, GoDaddy, Route 53, and Namecheap. See Bulk connect.
- Server-enforced roles — the role → permission matrix is now enforced at the control-plane, not just the console: API-key minting and member/role changes require admin or owner, billing requires owner. See Roles & permissions.
- Per-provider credential fields — the console's connect flow collects API credentials as separate labeled fields (with links to each provider's credential portal and numbered steps) instead of a colon-joined paste; used once and discarded by default, unless you opt in to remembering a key.
- Resumable setup — the Domains list's Finish setup reopens an abandoned guided connect flow at the right step.
- Four connect rails — OAuth into the DNS provider (flagship), provider-hosted
one-click setup (sync + async), BYO API-token apply, and guided-manual, all
converging on the same
pending → propagating → livelifecycle. - Broad DNS auto-write fleet — 38 built-in provider adapters (Cloudflare, Route 53, Google Cloud DNS, Azure DNS, GoDaddy, Namecheap, and many more) plus a full parity census that routes ~63 target providers to their working mode, and one-click setup for tokenless applies.
- Embeddable widget/SDK —
customdomain-js,window.customdomain, thecustomdomain:*events, ~90-token white-label theming, and embedded mode. - Signed webhooks — HMAC-SHA256 deliveries with a replay window, retries, and
a delivery log at
GET /v1/webhook-deliveries. - Billing & metering — a Stripe-backed plan catalog with metered domain usage and overage on the Growth tier.
- This documentation site — a proper information architecture with a generated, always-in-sync API reference (see how it stays in sync).