Skip to main content
Every project has a hosting address that the platform picks: a three-word handle served as HANDLE.check.omh.st for Prod and dev-HANDLE.check.omh.st for Dev. ohmyhost project status reports both. Before you name a specific address to someone, ask whether it is free:
The answer says whether the handle is available, why it cannot be used (taken, too_short, too_long, invalid_shape or prohibited_word) and returns up to five free alternatives. Agents reach the same answer through MCP project_handle_check or GET /v1/project-handles/{handle}. Once a handle is free, read the current project status and copy its project.etag value:
Move the project onto the new address:
$PROJECT_ETAG is project.etag from the CLI status response (etag in the REST or MCP response). Do not construct it yourself. Read it again before another rename; reuse the request key only when retrying the same rename. The command returns an operation: both gateways are re-published at the new address, and the new handle is stored only once Dev and Prod serve it. Follow it with ohmyhost operation get and wait for success before you quote the new URL. Agents use MCP project_handle_set or PUT /v1/projects/{project_id}/handle. The old address stops answering within about thirty seconds of a successful rename, and the name returns to the pool for any project to claim — links you already shared with it break, so rename before you publish an address, not after. A rename is refused while another operation is running for the project, for an address that is taken or unusable, and for the address the project already has. Update callback URLs, trusted origins and base-URL secrets that name the old host. A Dev share link or ticket from before rename points there; read the share link again after success. Managed Better Auth needs another deployment/promotion to update its platform base URL. If rename ends operation_abandoned, no address changed: read fresh project.etag and retry with a new key. A custom hostname requires Paid access, or a Free workspace whose project shows the ohmyho.st flag (below), and serves the project’s Prod deployment. Without Paid or the project flag, the custom hostname answers 402 Project execution paused; a funded platform address can keep serving. Use the real hostname you want to publish:
Review the plan, then apply it through domain_paid_apply or its CLI command. Save the hostname and idempotency key for the whole setup. If Prod is already active, this apply starts domain provisioning: verify the application on its hosting address first.
Without an active Prod deployment, apply reserves the hostname with status awaiting_deployment. It creates no DNS records or certificates and starts no custom-hostname charges; the hostname is not a live application URL yet. You can authorize Cloudflare for this reserved domain before deploying. Asking for Cloudflare authorization before the project has a matching reserved or provisioned domain returns cloudflare_zone_not_bound. Complete the first domain apply, then request authorization.

Cloudflare-hosted DNS

We prefer Cloudflare because the supported authorization flow can apply the required records:
Open the private authorization URL in the Cloudflare account that owns the zone. Read back the authorized zone and expiry. Consent is scoped to that project’s domain setup and does not change DNS. Deploy to Prod or promote a verified Dev deployment, then check the application on the Prod hosting address from project status. Neither successful deployment nor Cloudflare consent automatically activates a reserved domain. When the application works, repeat the original apply with exactly the same hostname and key:
The sequence is plan, reserve, authorize, deploy and verify Prod, repeat the same apply, then check domain status. The second apply provisions the hostname and applies the required DNS records when authorization is valid. Domain charges begin with provisioning, subject to the flag exemption below. DNS and HTTPS become ready separately; read their actual status. The access token renews automatically; authorize again only when status says expired or revoked. With valid scoped Cloudflare authorization, the confirmed Paid-domain apply replaces previous web A/AAAA or CNAME records at the requested hostname and adds its required verification records. At the zone apex, existing MX, TXT and CAA records are preserved. Other hostnames keep their current routing. Review the hostname in the plan carefully: this action moves its web traffic to the project’s Prod deployment. An incompatible delegation or another project’s managed DNS records returns HTTP 409 domain_dns_conflict. Repeating an unchanged conflict does not help. Review the records for the requested hostname, resolve the stated conflict while preserving mail records, then repeat the original apply with the same hostname and idempotency key. Do not create another deployment to repair DNS. DNS conflict guidance This flow connects one custom hostname per project. Connecting example.com leaves www.example.com unchanged. Multiple aliases and automatic apex/www canonical redirects are future work.

Other DNS providers

Reserve the hostname before deployment if needed. After Prod works on its hosting address, repeat the original domain apply to obtain validation_records: exact type, name and content from paid apply/status. No TTL is returned; use the DNS provider’s default. Add those records with your DNS provider. Preserve existing mailbox MX and unrelated records. You do not need to move your domain to Cloudflare.

Show the ohmyho.st flag

With the owner’s consent, a project can show a small “Powered by ohmyho.st” flag on the right edge of its production site. It loads nothing and links to ohmyho.st. While it shows, a Free workspace may connect its own domain to that project, the custom hostname costs no credits on any plan, and each Paid period bought through Stripe adds 250 credits once per workspace, however many projects show the flag. Granted/referral Paid days add none (25% more monthly credits for a paid period). Hiding the flag is refused with powered_by_flag_required while a Free workspace’s domain depends on it.
Only an Owner switches it through those commands, powered_by_flag_set or Show ohmyho.st flag on the portal project card; powered_by_flag_get reads it. On Free, show the flag before applying the hostname. After uncertainty replay the same key/value; every new switch needs a new key. It appears within about thirty seconds on Prod HTML a browser opens, so check it in a browser. Its https://ohmyho.st/?r=flag-<project> link counts as a referral while the flag shows.

Check readiness

DNS, HTTPS and mail verification have separate statuses. Mail status returns next_check_after_seconds (currently 60 seconds while pending); domain status returns no interval, so ask your agent to check again once DNS changes have had time to propagate. Keep the original deployment and do not rebuild to poll DNS. Email · Project context. When the final hostname is ready, configure it as the application’s trusted public origin and update its auth callback/logout URLs. Verify login and one protected action there. A host-only session cookie may require a fresh login after changing domains; do not widen cookie domains or trust arbitrary request hosts to mask incorrect configuration.

Move or retire a hostname

To move a domain to another project, run confirmed ohmyhost domain paid delete --project "$OLD_PROJECT_ID" --hostname "$HOSTNAME" --idempotency-key "$DOMAIN_DELETE_KEY" --yes --json, wait/read deletion, then plan/apply it on the new project. domain_hostname_taken means another project holds the hostname. An interrupted pending connection can also be deleted. This removes only the named connection and its owned DNS records, preserving unrelated mailbox MX. Deletion first reads the currently owned DNS records and removes only those records. It never creates missing records or adopts an unowned CNAME that you later recreated, even when that CNAME points to the same target. Unrelated MX, TXT and CAA records remain intact. Replay an uncertain deletion with its original key; if it returns domain_dns_conflict, resolve that specific conflict before retrying.