CustomDomain docs

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/orders returns your application's orders; unpaid quotes older than a day become expired (with purchase.confirmation.expired).
  • "Unregistered" only when it's true. The pre-flight reports registered: false only on an NXDOMAIN answer; a transient resolver failure omits the field and reports detection_error: resolver instead.
  • Webhooks say why. connection.failed (and any lifecycle event on a connection with a recorded diagnosis) now carries error_code and error_message. Connections also expose retried_at.
  • Widget tells the host why it fell back. A new customdomain:fallback event 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.
  • www works with the root redirect. A root connection with www_redirect now serves www.<root> too (the edge redirects the root there); previously www was refused at the edge and never got a certificate.
  • Rail truth on the pre-flight. POST /v1/domains:check now returns rails (per rail: available, and one machine reason when not), the server's recommended_rail, end_user_automatic, a blocked_reason for domains no rail can connect, a dashboard_url into the provider's DNS panel, and the observed nameservers. One-click setup is advertised only where the provider is measured to serve our template — never merely because it answered discovery — and domainconnect:start refuses what the check did not advertise, naming the provider. Hosting-platform subdomains (*.vercel.app and friends) are refused on create with platform_subdomain instead 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 pending connection 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.app and 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_failed with the provider's reason — not a 503.
  • Tenant-scoped connection webhooks — connection lifecycle envelopes now include the optional user_id echoed from the connection's end_user_ref, allowing integrators to associate events with the originating tenant or end user. Connections created without an end_user_ref continue to omit it.
  • Integrator-supplied record setsPUT /v1/connections/{id}/records replaces 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 livehttps://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 _dck1 selector (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 the DC_SYNC_PROVIDERS allowlist. 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 ⌘K command 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 → live lifecycle.
  • 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/SDKcustomdomain-js, window.customdomain, the customdomain:* 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).

On this page