> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ohmyho.st/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> For account actions, read https://ohmyho.st/skills/ohmyhost-get-started/SKILL.md and use the authenticated ohmyho.st CLI or local product MCP. Mintlify search only reads documentation. Preserve the customer’s selected project, environment and authentication provider.

# Connect a domain

> Choose a project address, prepare a custom domain before deployment, and connect it through Cloudflare or manual DNS.

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:

```sh theme={null}
ohmyhost project handle check --handle quiet-otter-waves --json
```

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:

```sh theme={null}
ohmyhost project status --project "$PROJECT_ID" --json
```

Move the project onto the new address:

```sh theme={null}
ohmyhost project handle set --project "$PROJECT_ID" --handle quiet-otter-waves \
  --if-match "$PROJECT_ETAG" --idempotency-key "$RENAME_REQUEST_KEY" --json
```

`$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:

```sh theme={null}
ohmyhost domain paid plan --project "$PROJECT_ID" --hostname app.example.com --json
```

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.

```sh theme={null}
ohmyhost domain paid apply --project "$PROJECT_ID" --hostname app.example.com --idempotency-key "$DOMAIN_REQUEST_KEY" --yes --json
```

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:

```sh theme={null}
ohmyhost domain cloudflare authorize --project "$PROJECT_ID" --zone example.com --idempotency-key "$DNS_AUTH_KEY" --json
ohmyhost domain cloudflare status --project "$PROJECT_ID" --json
```

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](/environments#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:

```sh theme={null}
ohmyhost domain paid apply --project "$PROJECT_ID" --hostname app.example.com --idempotency-key "$DOMAIN_REQUEST_KEY" --yes --json
ohmyhost domain paid status --project "$PROJECT_ID" --json
```

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](/errors/domain-dns-conflict)

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.

```sh theme={null}
ohmyhost project flag status --project "$PROJECT_ID" --json
ohmyhost project flag set --project "$PROJECT_ID" --enabled true --idempotency-key "$FLAG_REQUEST_KEY" --json
```

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

```sh theme={null}
ohmyhost domain paid status --project "$PROJECT_ID" --json
ohmyhost mail status --project "$PROJECT_ID" --environment "$PROD_ENVIRONMENT_ID" --json
```

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](/email) · [Project context](/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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.