Console, connect your own domain
Connect your own domain with no code: sign up, tell us who the domains are for, add the DNS records, verify, and see the domain serving your app over HTTPS.
Use the console when the domain is yours and you want it to point at something you run. If your customers should connect their domains inside your product, that is the widget, not this page.
Two different people connect domains, and the console shows which did:
| Who | Where | What they see |
|---|---|---|
| Your customer | Inside your product, in the widget | The widget, in your branding. The domain appears under Domains as From your product (widget). |
| You or a teammate | Here, in the console | The console connect flow this page describes. The domain appears as Added in the console. |
Your server, AI agents and MCP clients can create domains through the API too; those are listed as Via the API or agents.
1. Sign up and verify
Sign up at app.customdomain.ai. A verification email is sent and its link (valid for one hour) must be clicked before you can reach the console: unverified accounts can't sign in or create API keys. You'll see a Check your email screen with Resend the link, and if your link expired, signing in re-sends a fresh one automatically. Google and GitHub follow the same rule: if the provider has not confirmed your email address, you land on Check your email and the link we send finishes the sign in. A provider that has not confirmed the address also cannot sign in as an existing account that has it: sign in with your password, then add Google or GitHub under Settings, Security (the provider must have confirmed the address for that too).
Tick the box under the heading to agree to the Terms of Service and Privacy Policy. Email, Google and GitHub all wait for it. If you already had an account before the box existed, or you came in through an invite or a Google or GitHub sign in from the sign in page, the console asks once, on a page of its own, and then takes you where you were going.
The signup form also carries bot protection: a bot-check widget, signup rate limits, and disposable-email-address blocking. If the bot check fails, wait a moment and try again.
Signed up with an email and a password? Once the welcome is done, Overview offers once to add Google or GitHub as another way to sign in: Add Google, Add GitHub or Not now. It only connects an account that uses the same email address as yours, and either answer is remembered on every device. You can do the same any time under Settings, Security.
2. Tell us who the domains are for
The first time you sign in, the welcome asks one question: who are the domains for? It is one click, and it sets up the steps that suit you. The question needs an answer. Every step after it has Skip to dashboard at the bottom right, which takes you to the console; what is left stays on the checklist on Overview, and Change my answers there opens the welcome again. A step whose own button does the work also has Not now, which moves on to the next step.
| You pick | The steps after the question |
|---|---|
| My customers (you run a product and want your users to bring their own domains) | About you, your brand with a live preview on the real widget, then who writes the code: I am writing the code or My AI writes the code. The same screen shows your application ID and your default key to copy. Then the widget code, or for your AI, the setup for the agent you use and one prompt to copy (it has your application ID, never your key). That is the last step |
| My clients, or many of my own | A box to paste your domains into, with a client's name beside each (or one domain, if that is all you have). See Many domains, and whose they are |
| Just me (a domain you own) | Your domain, with an option to set up email on it in the same step |
| Not sure yet | A short list of things to try (connect a domain of your own, preview the widget with your brand, let an AI agent connect domains), then the path of the one you pick |
I am writing the code shows the widget code in four steps, each with a copy button: your key, a token route on your server, the widget for your framework with your application ID filled in, and the result. Then pick where to keep building, or Try it now.
Under the cards, Also let an AI agent connect and manage domains for me adds one more step, Give your agent access, to any of them. Pick the agent you use (Claude Code, Claude Desktop, Codex, Cursor, VS Code, ChatGPT or another), add CustomDomain™ to it with a command or a few fields, and paste in the prompt the step gives you, which says what to do first for what you told the welcome. Most agents sign in with your account, so there is no key to paste, and the step says connected only once the agent signed in and you approved what it may do. It is optional: Not now moves on. See MCP: drive it from an agent and Agents.
Pointing a domain at a website needs the Growth plan: CustomDomain™ does not host websites, it serves your domain through its edge to a site hosted elsewhere (see Domain routing). The Just me card says so before you choose it. Connecting the domain does not need it.
Everything that can be worked out is filled in for you to confirm. Your company name and website come from your sign-up email (not from free mail such as Gmail, where it asks for the website instead), your logo, name and brand color are read from your website, and for your own domain it offers the one your work email is at. Change anything that is off.
The domain step finds where the domain is registered and hosted as soon as you type it, says whether we can set it up automatically or will show you the records, and opens the same connect widget your customers use. If it is not registered yet, it lists names close to it that you can buy, with the first year price, and links to the console's domain search to buy one. If you have no domain yet, I don't have a domain yet offers that search, or carrying on to connect one when you have it. When something is left, it says what and who can do it: anyone with access to your DNS host can finish it.
Your place and answers are saved on every step, so closing the tab and coming
back resumes where you left off. Each step has its own address
(/welcome?step=brand), so a refresh stays on it and the browser's Back goes
where the Back button does, and a domain you already connected shows as
connected. The last screen shows what you have and the one next action, with a
few more to start with. While a domain waits for its DNS records it also offers to
ping you when it is live, with Notify me and Not now: your browser asks only
when you click, and it turns push on for a domain going live and nothing else (see
Notifications). Open the welcome again any time to change who the domains
are for: from the first row of the checklist on Overview, or from Change my
answers under the go-live list if you picked My customers.
A short tour of the console follows. Replay it any time from the setup checklist on Overview. The checklist follows what you picked: for your own domain, connecting it (and setting up email, or pointing it at your site, if you chose those); for a few clients, connecting the first domain; always ending with inviting your team. If you asked for an agent, an optional Connect your agent row is done when an agent has signed in and been approved, and says so by name; an agent that uses a key leaves nothing to see, so the row says that too. If you picked My customers, the checklist is the go-live list below.
Your go-live list (My customers)
If you run a product and want your users to bring their own domains, Overview shows a go-live list. A row is done when something has happened that proves it, not when you visited a page, and each row says what was checked:
| Row | Done when |
|---|---|
| Start your free trial (owners and admins) | Billing is set up and a trial or a plan is running. A card starts the 14 day trial and nothing is charged until it ends. Creating API keys needs it |
| Run your first connection (then Try the widget with your own domain) | A domain connected through the widget is live. A real domain counts as one of your domains, which is why this row says so. Start opens four ways to begin: see the widget as your customers do, connect a domain you own, try the whole flow in test mode on made up .test names (they use nothing), or copy a prompt for your AI agent with your application ID. The same choice opens once by itself after the welcome; Skip closes it for good, and the row stays open until a real domain is live |
| Mint widget tokens on your server | Your backend asked for a widget token with an API key (the route from the Install tab, written for your backend). A token minted from the console's Try it live does not count |
| Choose where customers' domains point | Your own infrastructure is set as the target, or a default origin is set for the edge (which needs the Growth plan) |
| Get told when a domain goes live | A webhook delivery was answered with a success status |
| Make the widget yours (optional) | A look was saved, for the workspace or for an application |
| Connect your first customer | A real customer connected a domain through your widget, which is a connection that carries the customer id your server passed when it minted the token |
Test mode is a separate application you switch to from the application switcher, marked Test mode wherever it appears. It needs no card, it is not counted or billed, and its domains show on Domains and Activity only while you are switched to it. See Test mode for what each ending does and what it leaves out.
A row can be done and still warn: a default origin that failed its check, or a webhook whose newest delivery failed, each with a link to fix it. The list stays on Overview while any row warns, even when every required row is done, and it reads again when you come back to the tab, because most of these steps are finished somewhere else. A customer who connects makes the earlier rows done as well, since they cannot have connected without the widget opening and your server minting a token. A token minted straight at the API with your client secret is not seen by the console; your first customer proves it instead.
The list follows what the workspace's owner or an admin said: a teammate you invite sees the same go-live list (without the trial row, which is for owners and admins). In a workspace for clients or for your own domain, a teammate sees the usual checklist until they answer the welcome too. Your customers' domains are on Domains, on the From your product (widget) tab, which is the tab it opens on.
The console at a glance
| Section | What it's for |
|---|---|
| Overview | The setup checklist and how your customers' domains are doing. |
| Domains | Every connected domain with its live status and exactly what is left to fix. Open one for its DNS, Certificate, Edge, Traffic and History tabs. |
| Analytics | The widget funnel on its own tab, with a Traffic tab for what your domains receive at the edge (and the busiest domains) and a Providers tab for where customers drop off by DNS provider and by root versus subdomain. |
| Widget | Install (code for your framework, with the Authenticate with switch for the token step), Design (the widget itself, every screen, light and dark, with a look for each application) and Try it live. |
| API & SDK | Server-side API keys (the workspace's default key at the top, Create key for more), with a Webhooks tab and a Guides tab for the first request and the SDK. |
| Domain routing | Where customer domains send traffic. |
| Connected accounts | The DNS provider keys and the sign ins we keep so your domains can be changed for you. |
| Settings | Account, Security, Notifications and Privacy, and the optional features. Team, Billing, Usage and Applications open under it. |
API keys and widget tokens
Open API & SDK. Every workspace has a default key at the top of the page,
made with the workspace and needing no card: owners and admins can view it at any
time (see API keys). For another key, press
Create key, give it a name and pick an Environment: Production
(cd_live_…), Development or Staging (both cd_test_…). The full key is shown
once ("Copy your new key now"); only a hash is kept. Admins and owners can create
keys. Creating one needs a card on file, which starts the 14 day free trial
(nothing is charged until it ends). Your first ten domains are free without a
card, but another key is not, so a workspace without a card that presses Create
key sees "Start your free trial to create API keys" with an Add a card
link, and the key is created once the card is in.
The application the console made for you has a client secret, but the console
keeps it sealed and never shows it. To mint widget tokens from your server, send
one of these keys to the console's POST https://app.customdomain.ai/api/widget-token;
the Install tab on Widget writes the code for you. Do not rotate that
application's secret: the console's copy would go stale and the preview, Try it
live and that endpoint would stop working. See
Widget tokens.
Optional surfaces (the activity log, DNS monitoring, domain sales, the MCP server and agent access) are off until you need them: switch them on under Settings → Features. A workspace that already used one keeps it on, and agent access switches on by itself when you approve an agent.
The console follows who the domains are for
The answer to the welcome's first question is kept for the whole workspace, and the console lays itself out by it. Nothing is hidden or removed: every page is still one click away. The answer changes the order of the menu, what the main button does, and what Overview and Domains lead with.
| My customers | My clients, or many of my own | Just me | |
|---|---|---|---|
| Menu | Overview, Widget, Domains, Analytics, then API & SDK, Domain routing and Connected accounts | Overview, Domains, Connected accounts, Analytics, then Domain routing. Widget and API & SDK are under Integrate: click its name to open it | Overview, Domains, Analytics, then Domain routing and Connected accounts. Widget and API & SDK are under Integrate |
| Main button | Open the widget | Connect a domain, straight to your own domain: it does not ask who is connecting it | The same |
| Overview | The go-live list, then your customers' latest connections and the widget's week | The domains that need attention first, and which DNS providers they are with | With one domain, that domain: its status and the next step. With two or more, the counts |
| Domains | Opens on From your product (widget), or on All until a customer has connected one | Opens on All | Opens on All |
| Empty Domains page | "Your customers' domains appear here", with the widget as the way on | "Add your domains", with a button to paste a list | "Connect your domain" |
The counts on Overview include every domain, so when they link to Domains in a workspace for your customers they open the All tab, and what you click is what you see. The first-run tour follows the same layout. A workspace that has not said who its domains are for, or picked Not sure yet, gets the console as it has always been.
Change it any time under Settings → What this workspace is for. Owners and admins can change it; it changes the console for everyone in the workspace, and everyone else sees it read only. A teammate you invite sees the same console you do, whatever they answer in the welcome.
The bell at the top right holds your latest notifications: a domain going live, a domain that needs attention, a failed payment, and more. Turn on push under Settings → Notifications to get them on your phone or computer too. See Notifications.
Applications
The Application switcher at the top of the sidebar is always there, even with a single application. It lists your applications and offers New application and Manage applications. Use more than one to keep surfaces apart: Production and Staging, or one per product. Each application has its own widget credentials, API keys and domains, and the console shows the active one; switching is remembered in your browser.
Viewers see applications and their domains but cannot create one or use its
widget, and the console says so. Buying a domain, bulk connect and the widget's
token need the member role or above.
New application asks for a name (with one-tap suggestions such as Staging) and an environment (Production, Staging or Development). Once it exists you can switch to it or go straight to its widget.
Each application has its own page: open it from Applications or from the
switcher. Its summary counts the application's domains by status, and its
Domains (the ones that need attention first, each opening its details),
API keys, Routing and Settings (the widget, Rename, Switch to
it and Delete application) are tabs. Deleting asks you to type the name, revokes the application's
API keys, and is refused for your workspace's default application and for one
that still has domains (disconnect them first). An application created through
the API rather than in the console works the same, except that its widget
can't be previewed here: its client secret was issued to whoever created it
and was never stored in the console, so mint its tokens with POST /v1/tokens
and that secret.
3. Connect a domain
Connect a domain (the sidebar button, Overview or Domains) first asks who is connecting it:
- Connect a domain yourself: your own site, or one you're setting up for a customer. You add the DNS records. This is the console path, and the rest of this page.
- Let your customers connect theirs: takes you to the Widget page to embed it in your product.
Links that already say which (a domain's Resume link, or Connect a domain yourself on Overview) skip the question. The connect page then says plainly that you are on the console path. Enter the hostname:
www.acme.com/app.acme.com: a subdomain. Any DNS provider can hold theCNAMEwe need.acme.com: the root. It works everywhere, but the record depends on the provider; see Root domains.
The pre-flight refuses two things up front: a domain that doesn't resolve at
all (unregistered, or never delegated at the registrar) and a hosting
platform's own subdomain (www.<name>.vercel.app). Both used to sit on
"waiting for DNS" for days.
4. Choose how to add the records
The options shown are the truth for your provider, straight from the server. They appear in this order; the recommended one is marked.
- Sign in with the provider: Cloudflare, DigitalOcean, DNSimple, Linode, Netlify, Vercel (by installing the CustomDomain™ integration) and WordPress.com, plus Approve access at Porkbun. Approve once in a popup and we add the records. Fastest. Keep this sign-in starts ticked: we keep it, encrypted, so we can put the records back and remove them with the domain; untick it to use the sign-in once. Sign in with Azure DNS (a Microsoft account) and Sign in with Dynadot or Unstoppable Domains (through the provider's MCP server) are used once. OVHcloud's consent page and the other MCP server sign-ins are built but not switched on yet; see DNS providers.
- One-click setup: only where the provider serves our template (NameSilo, Glauca Digital, Domain Chief today). After you approve the change there, the provider sends you back here. GoDaddy, IONOS, Aruba, Squarespace, Cloudflare and Vercel are not offered this, on purpose: they have not onboarded our templates.
- API key: paste a scoped provider credential, one labeled field per part, with a link to where it is created, the steps and the one requirement that matters; anything else, and the provider's notes, sit under a closed Details. We keep it encrypted when Remember this key stays ticked (untick it to use the key once). Any GoDaddy account that holds a domain can create a Personal Access Token; Namecheap's API needs an eligible account and our server's IP on its allowlist. Route 53 offers an access role first, with no key to paste.
- Add the records by hand: the exact rows to copy, with a link to your provider's DNS panel. Then Check now.
Not the one with DNS access? Under the records, Send these records to a teammate emails them to whoever is. Replies go to you.
- An owner or admin sending to someone who isn't in the workspace also invites them as a member; the email carries the records and the invite.
- Someone already in the workspace gets the records and a link back to this domain's setup in the console.
- A member can send the records but not invite: the email links a single-use guided setup that needs no account.
- Viewers can't send email from the console.
Someone outside your team, such as a client who has the DNS? Use Copy a link for someone outside your team instead. It makes a setup link for this one domain that works with no account and invites nobody; see A link for the client.
5. Verify
The connection sits in pending until the records resolve, then goes
live. Records written for you (a sign-in, one-click setup or an API key)
usually verify within seconds: a new connection is checked every five seconds
at first, and when public DNS still has an old answer, we ask the domain's
own nameservers instead. You can leave and come back:
Domains shows every connection with its reason and a Resume link.
After three minutes of waiting the page offers a fallback. A stalled
connection is paused, not failed: it keeps being checked every six hours.
Every domain in your workspace opens on its detail page, whichever application it belongs to; the page names the application. A domain that isn't live leads with What happened: what the last attempt did (it timed out, the provider refused a key, it is still waiting for the records, or it is connected somewhere else), the next steps in order, and the action that fits: Finish setup, Retry (which restarts a failed attempt), or removing an attempt nobody will finish. Below it, a short history says when the domain was added and through what, and when its DNS was last checked.
While a domain is waiting, its detail page offers Get a ping when it is live
(owners and admins), so you can close the tab and be told the moment it connects;
see Notifications. It also opens What public DNS shows
right now: each record's Name to type, whether public DNS has it, what
answers instead ("still points to Squarespace"), and the mistakes that block
a connection even when a record looks right, such as the full hostname typed
into the Name field or Cloudflare's proxy turned on. It is the same check the
widget runs for your customers (GET /v1/connections/{id}/diagnose), and the
page to send a customer whose domain is stuck.
6. Serve something
Live means the edge terminates TLS and proxies the domain to your
application's origin. If you haven't set one, the domain shows a "not
configured" page: set it under Domain routing. Root-domain redirects
(www_redirect) and per-path rules live on the connection's detail page.
7. See its traffic
Once the CustomDomain™ edge serves a domain, its detail page has a Traffic tab: requests by the kind of answer visitors got (success, redirects, client errors, server errors), bandwidth sent to visitors, how fast your origin answered (p95 and median time to first byte), approximate visitors, and when the last request arrived, over 24 hours, 7, 30 or 90 days. Analytics has a Traffic tab for the whole workspace (or one application), with the busiest domains.
Only traffic that passes through the edge is counted. A domain connected for DNS only points at your own servers, so its visitors never reach us; its page says so instead of showing zeros, and your hosting has those numbers.
Counts arrive about a minute after each visit. No visitor's IP address is
stored: visitors are estimated from a sketch of keyed hashes, and someone who
visits on two days counts once on each. Hourly detail is kept 14 days and
daily totals 400 days. The API has the same figures:
GET /v1/connections/{id}/traffic and GET /v1/traffic.
Many domains, and whose they are
If you manage domains for clients, or have a lot of your own, you don't have to connect them one at a time. The welcome's first screen for My clients, or many of my own is a box: paste a column from a spreadsheet, a line of addresses or a list of links, in any mix. The same box is on Domains under Add several, up to 200 at a time.
- What it reads. An address gives its host, a repeat is added once and counted, and anything that is not a domain is listed and left out. Nothing you typed is thrown away: what could not be added stays in the box.
- Whose it is. A name typed above the box is shown next to every domain
below it. A name after a comma or a tab on a line (
beta.org, Beta Bakery) is used for that domain instead. A domain already in your workspace is left as it was. The name is set when the domain is added and cannot be changed afterwards, so to rename a client or give a domain to another, remove the domain and add it again. - The first ten domains are free. A new workspace connects its first ten domains without a card, and the rest once the free trial has started. The box says so before you paste, with Start free trial beside it (nothing is charged for 14 days). It adds what it can now and keeps the rest in the box, and after the trial starts you land back on the same page with the rest still there. A teammate who is not an owner or an admin is told to ask one of them.
- Adding is not connecting. A list puts the domains in your workspace, each waiting for its DNS records. Finish each the way you would finish one, from its row on Domains, or send the step to the person who has the DNS (below).
On Domains, a workspace for clients says client where the others say customer:
- Client narrows the list to one client's domains, or to those with no client.
- Order shows newest first (the default), oldest first, domain, client, or what needs attention first.
- Export CSV saves exactly the rows shown, in the order shown: domain,
status, client, DNS host, when it was added and from where, the application,
and what is left to do. It opens in a spreadsheet. A client name that starts
with
=,+,-or@is saved as text, so a spreadsheet never runs it.
A domain's own page says "For Acme Dental" in a workspace for clients, where it would say who connected it elsewhere.
A link for the client
Send these records to a teammate also invites its recipient to the workspace when an owner or an admin sends it, which is wrong for a client who has the DNS. Copy a link for the client (in the ⋯ menu of a row on Domains, and beside the email option on the connect step; it reads Copy a setup link in other workspaces) makes a link for that one domain instead:
- Whoever opens it finishes that domain with no account, and nobody is invited. The page carries your workspace's name and nothing about its billing.
- It works once and lasts 30 days. It cannot be listed or cancelled afterwards, so make it when you are ready to send it. The console shows it on the page as well as copying it.
- It is for a domain that is not live yet, and it needs the member role or above. A workspace can make 30 an hour.
Where each domain came from
Every row on Domains carries a small badge for where it was created, and the tabs above the list filter by it (counts are for the selected application):
| Tab | Badges | Created by |
|---|---|---|
| From your product (widget) | Widget | A customer, in the widget inside your product. |
| Added in the console | Console | You or a teammate, here. |
| Via the API or agents | API, Agent, MCP | Your server with an API key, an AI agent you authorized, or the MCP server. |
The first visit explains the two paths with the same small diagram the
Connect a domain question uses. Domains added before sources were
recorded (September 2026) have no badge and are listed under All only.
The API returns the same fact as created_via on every connection
(widget, console, api, agent, mcp, or empty when not recorded). The
Overview shows the split too, under the counts.
Design the widget
Widget → Design is where the widget gets your look. The preview is the widget itself, the one your customers get, running on example data: nothing here is sent anywhere, and no domain or connection is needed.
Step through the journey with the row above the preview (enter the domain, detect the provider, sign in, apply, connected), open the other screens under More screens (existing records, manual setup, email records, an error and more), and pick the steps of a checklist. Each screen says which status it reports to your code. Look at the widget in light or dark, on a computer or a phone, and in any language it speaks.
| Group | What you set |
|---|---|
| Brand | The name in the first heading ("Connect your domain to …"), the logo, a separate logo for dark mode (or a light plate behind a logo drawn for light pages, which the designer offers in one click when a logo would be hard to see on the dark surface), the logo's size (16 to 48 pixels, 32 by default), and whether "Powered by CustomDomain™" shows. |
| Colors | The brand color for buttons, links and focus rings. Text on it is chosen to be readable, and it is lifted in dark mode when it would vanish. An optional background and text color, used in both light and dark. |
| Shape and type | One corner radius (buttons, fields, cards and the pop-up follow it in proportion), a system font, the pop-up's width on a computer, and its shadow. |
| Behavior | Light, dark or match the device; the confetti; the close button; the progress steps. |
| Screens and options | "Set up manually instead" on the sign-in screen, "Let an AI assistant do it", "Email these instructions", and the offer to host DNS with CustomDomain™. |
Each application has its own look. The designer edits the application chosen in the sidebar. Save gives that application a look of its own. Save as the workspace default (under the ⋯ menu) sets the look every application without its own uses, and Use the workspace default for this application drops the application's own look. Saving needs an owner or admin. The install code and Try it live use the saved look; the preview shows what you have not saved yet. The install code carries only what differs from the widget's own look, so an untouched color or corner radius is not written into it.
For the logo, Upload (or drop a file on it). PNG, JPEG, WebP, GIF (its first frame), AVIF and SVG are accepted. Your browser prepares every upload the same way, so logos look consistent: fully transparent edges are trimmed, the image is fit inside 512 by 160 pixels (256 by 80 on screen at 2x), and it is saved as a PNG under 200 KB. An SVG is converted to a PNG; the SVG itself is never stored. Each logo is shown on the surface it will sit on, and you'll see a warning when it would be hard to see there, or when it has a solid background that would show as a box. The upload is hosted at once and appears in the preview; it belongs to the application when you press Save.
Uploaded logos are served from https://app.customdomain.ai/brand/logo/…
at a URL named for the file's content, so it never changes and is cached
for a year. Replace uploads a new one; Remove clears it from the design.
Earlier uploads stay available at their URLs (your 20 most recent, and any an
application still uses), because a widget you already shipped keeps the logo
URL it was pasted with until you update its code. You can still paste the URL of
a logo you host yourself (HTTPS) instead.
What the console is not
It is one application in your workspace. Everything it does is an API call you could make yourself (the API page maps each step), and its Widget and API & SDK pages are where the other paths begin.
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.
API, provision domains from your server
Provision custom domains from your server with an API key: pre-flight check, create, apply records, verify and react, plus mistakes integrators actually make.