Guides

Custom domains

AdminUpdated Sep 19, 2026

Custom domains

Serve your help center from your own hostname — with CNAME or TXT verification, Domain Connect one-click setup, and automatic TLS.

By default, your help center lives at a path under Chatly's help host — https://help.chatlychat.com/{workspaceId}. Most customers want their own hostname for trust and brand consistency: help.acme.com.

Warning — Custom domains cover the help center, and only the help center

There is one domain kind: help_center. Chatly briefly offered widget, marketing, and email kinds as well; none of them was ever served, so a customer could verify marketing.acme.com and find nothing there forever. They were removed in migration 0384. The widget loader and inbound email always run on Chatly's own hostnames.

This page walks through claiming a hostname, verifying it, and what happens after.

Claim a hostname

Dashboard

  1. Workspace → Domains → Add domain.

  2. Type the full hostname you want served — help.acme.com, not acme.com. Chatly does not add a prefix for you.

  3. We return the CNAME target and a TXT challenge, plus a one-click button if your registrar speaks Domain Connect.

API

POST /v1/domains — Bearer token

POST /v1/domains
Authorization: Bearer …
Idempotency-Key: 3d90…
Content-Type: application/json

{ "hostname": "help.acme.com" }

Response:

{
  "id": "01920f3c-...",
  "hostname": "help.acme.com",
  "kind": "help_center",
  "status": "pending",
  "txtRecord": {
    "host": "_chatly.help.acme.com",
    "value": "chatly-domain-verify=abc123def456"
  }
}

Request

Name

Type

Description

hostname (required)

string

3–253 characters, letters/digits/dots/hyphens only, lowercased on write. The whole hostname to serve.

Claiming a hostname that already exists re-issues its verification token and puts it back to pending. Hostnames are unique across the platform.

The records to add

GET /v1/domains — Bearer token

{
  "cnameTarget": "connect.chatlychat.com",
  "domains": [ { "id": "…", "hostname": "help.acme.com", "status": "pending", "…": "…" } ]
}

cnameTarget is the value to publish. Read it from the API rather than guessing — it is deliberately not the help host, which sits behind a proxy, and a customer pointed at the wrong one lands on a CDN with a hostname that is not in our zone.

Records

Name

Type

Description

CNAME (primary)

help → cnameTarget

This is both the routing and the proof. A CNAME that resolves to cnameTarget (or a subdomain of it) verifies the hostname on the next sweep.

A (apex only)

acme.com → cnameTarget addresses

An apex cannot legally hold a CNAME. An A record matching the target's own addresses is the equivalent proof.

TXT (escape hatch)

_chatly.help → chatly-domain-verify={token}

Proves control without routing traffic, for a hostname you cannot repoint yet. Accepted as verification — but a TXT alone never makes the domain live, because nothing is pointing at us to serve it.

Verification

Verification is done by a background sweep, not by the request you make.

POST /v1/domains/{id}/verify — Bearer token

{ "queued": true }

That is the honest answer this button can give: it moves the row to the front of the sweep's queue. It used to resolve DNS inline and report the result, which made a click feel authoritative when it could not be — DNS propagates somewhere between five seconds and an hour after a record is saved, so an immediate check almost always ran inside that window and told someone who had done everything right that they had failed.

The sweep re-checks on a backoff ladder — 30s, 60s, 2m, 5m, 10m, then every 30m — and once a domain is live it re-checks every 6 hours.

Verify by Domain Connect (one click)

If your DNS registrar implements Domain Connect, the dashboard shows a Set up at your registrar button instead of the copy-paste records:

GET /v1/domains/{id}/domain-connect — Bearer token

{ "supported": true, "applyUrl": "https://…", "providerName": "GoDaddy" }

Click it → land on your registrar → see the exact records that will be applied → confirm → records appear in your zone. { "supported": false } is a normal answer; the page keeps showing the records to copy.

After verification

Once the sweep proves the hostname:

  1. A Traefik router for that host is published — the certificate cannot be issued for a hostname the proxy has no router for, so this happens before the certificate is probed.

  2. Traefik issues a Let's Encrypt certificate on demand, via the resolver named by TRAEFIK_CERT_RESOLVER (default letsencrypt-http). Renewal is automatic.

  3. Requests for that host resolve to the workspace's help center. The renderer maps the Host header back to the workspace, and to the non-default portal if the hostname is bound to one.

The first request after verification may take a few seconds while the certificate issues. After that it's instant.

Status and diagnosis

GET /v1/domains returns a status (pending / verified / revoked), a cert_status, a verification_method (cname / address / txt), and a diagnosis — which is the field worth reading, because "the record is missing", "the record points somewhere else", "a proxy is in the way", "your CAA record forbids our certificate authority", and "DNS is perfect, the certificate is still issuing" used to all be one word.

diagnosis

Name

Type

Description

awaiting_dns

waiting for DNS

Nothing published yet, or not propagated. The overwhelmingly common state for the first few minutes, and not an error.

points_elsewhere

points somewhere else

The hostname resolves, but not to us. Check that the CNAME target matches cnameTarget exactly.

proxied

proxy is in the way

The zone is on a provider whose proxy mode hides the origin. Turn proxying off for this hostname (DNS-only) so validation can reach us.

caa_blocked

certificate blocked

A CAA record on the domain lists which authorities may issue, and ours is not among them. Add a CAA record permitting letsencrypt.org, or remove the existing one. Re-checked hourly rather than hard-polled — this needs a human to edit DNS.

cert_pending

issuing certificate

DNS is correct and the certificate is being issued. Nothing for you to do.

cert_failed

certificate failed

DNS is correct but issuance did not complete — usually CAA, or a proxy intercepting the validation request. We keep retrying.

live

live

Serving. Re-checked every 6 hours.

TLS certificates

Certificates are issued and renewed by Traefik against Let's Encrypt. There is no endpoint for uploading your own certificate or key — a wildcard cert you already hold cannot be attached to a Chatly custom domain.

Removing a domain

DELETE /v1/domains/{id} — Bearer token

Deletes the row. The Traefik router for that host is dropped on the next sweep, and the certificate simply expires. Re-adding the same hostname later starts the flow over with a fresh verification token.

Warning — Make sure no traffic still depends on it

Links you have published to help.acme.com stop resolving to your help center. Point people at the default help.chatlychat.com/{workspaceId} URL before deleting the hostname, not after.

Troubleshooting

Warning — I added the records but the status is still pending

Verification is a background sweep, not the button. Give it a cycle — the first few retries are 30s, 60s, 2m apart. If it stays awaiting_dns, run dig CNAME help.acme.com +short from your own machine; an empty answer means the record is not authoritative yet. Some registrars take ~30 minutes despite the TTL.

Info — Diagnosis says points_elsewhere

Compare your CNAME's value against cnameTarget from GET /v1/domains. The two most common mistakes are pointing at the dashboard's hostname and pointing at the help host — neither serves a custom domain.

Info — Cert issuance stuck on 'issuing certificate'

Check for a CAA record first (dig CAA acme.com +short) — the sweep reports caa_blocked when it can see one, but a CAA at a parent zone can still bite. Otherwise the Traefik log (docker logs traefik | grep acme on self-host) shows the exact ACME error.

Was this page helpful?