CustomDomain™ docs
Getting started

Which path are you on?

CustomDomain™ has four front doors: the console, the API, the embeddable widget and the MCP server. Pick yours, then read the truths every path shares.

CustomDomain™ is one product with four front doors. The model underneath is the same everywhere: a workspace has applications; an application owns connections (and its origin, keys and webhooks); a connection is one domain moving pending → propagating → live; live means the edge terminates TLS and proxies the domain to the application's origin.

You are…UseStart
Pointing your own domain at your appthe consoleConsole (no code)
A product that provisions domains for customers server-to-serverthe API (/v1, cd_ API key)API
A product whose customers connect their domains inside your UIthe widget (customdomain-js)Widget
An AI agent or MCP clientthe MCP server (mcp.customdomain.ai)MCP
Building one of the above before you have a customer's domaintest mode, made up .test names, no cardTest mode

Shared truths

These hold on every path. If a screen or a response seems to contradict one of them, that is a bug. Tell us.

  • Four statuses. pending (records not yet seen), propagating (a rail wrote them, we are watching DNS), live, failed.
  • pending has no deadline. Checks run every five seconds at first and back off to every 15 minutes; after 72 hours the connection stays pending with error_code: setup_incomplete and is re-checked every six hours, forever. It goes live the moment the records appear.
  • Records written for the user usually verify in seconds. A rail's write is checked at once, and when public resolvers still hold an old answer we ask the zone's own nameservers.
  • propagating fails after 24 hours (error_code: propagation_timeout): records were written but never resolved. Usually a wrong value or a Cloudflare proxy (orange cloud) in the way.
  • failed is not terminal. Resume / :recheck revives it; re-creating the same domain revives it (200); real traffic at the edge revives it.
  • The diagnosis is on the connection: error_code and error_message on GET /v1/connections/{id}, on every list, and on the webhook events.
  • Test mode runs all of this on made up names. A test mode application connects .test names, goes live, stays pending or failed on a short simulated clock chosen by the name, sends the same webhooks marked "sandbox": true, and counts for nothing: no free domain, no allowance, no bill.
  • HTTPS only after live. The certificate issues on the first request once the connection is live; a domain pointed at us before that gets a clear "not active yet" page, never a raw error.
  • Root domains work everywhere, but the record differs by provider (ALIAS/flattened CNAME vs A records); see Root domains.
  • "Automatic" means an adapter exists, not "the user types nothing". Read rails / recommended_rail / end_user_automatic on the pre-flight (see Rail truth).
  • Drift monitoring watches live connections hourly; alerts are not delivered on the hosted service yet.

Which credential do I have?

You got it fromIt isUse it for
The console → API & SDKa cd_live_… / cd_test_… API key (tenant-scoped; keys created earlier start sk_ and still work). Every workspace has a default key from the start, which owners and admins can view at the top of the page at any time. Another key from Create key is shown once and needs a card on file (14 day free trial)server-to-server API calls, the MCP server, and minting widget tokens through POST https://app.customdomain.ai/api/widget-token
POST /v1/applications (an application created over the API)the application's client secret (sk_…, shown once in that response). A console signup never shows oneminting widget tokens from your server with POST /v1/tokens
A widget token endpoint (above)a short-lived widget JWT (one application, optionally one domain, optionally one end user)the browser: customdomain-js open({ token }), or direct /v1 calls from the page
An agent granta delegated agent tokenthe agent's scoped API calls

Most customers sign up in the console, so they hold an API key and mint widget tokens through /api/widget-token; the client secret path is for applications created over the API. An API key is not a client secret and the two are not interchangeable. See Widget tokens.

A widget JWT can never mint another token, never read another application's data, and never choose the records we write. Anything that chooses records requires an API key.

On this page