REST API reference

Custom domain

A custom domain puts your help center on your own address, such as help.acme.example. Set the domain, add the DNS record the answer gives you, and follow the domain's checks until it is live. These endpoints manage the same domain as Custom domain in Settings (see Use your own domain).

Endpoints

Method and path

What it does

Scope

GET /v1/custom-domain

Get the custom domain and its checks

domain.manage

PUT /v1/custom-domain

Set the custom domain

domain.manage

DELETE /v1/custom-domain

Remove the custom domain

domain.manage

All three act on the help center of the credential: there is no help center id in the path. An API key holds domain.manage only when one of the help center's Owners or Admins has ticked Allow managing the custom domain for it in Settings, under API keys: when they create the key, or later with Edit access on it, which keeps the key and applies from its next request. Only a Read & write key can hold it. Any other key gets 403 Forbidden with insufficient_scope on all three, GET included, and the message names that setting. A key works only while the person who created it is an Owner or Admin of the help center, the people who may change its domain in the dashboard.

An OAuth app asks for domain.manage, and the consent screen shows the person "Connect your help center to your own domain, check its DNS and certificate, and remove the domain." The token then works only while that person is an Owner or Admin of the help center: anyone else gets 403 with role_forbidden. An OAuth token names its help center as shown in Choose which help center a request acts on. The setting under API keys is for API keys only: a token holds the scopes its person approved. See Scopes.

How a domain goes live

  1. Set the domain with PUT /v1/custom-domain. dns_records holds the one record the domain needs, and status is pending_dns until that record is in place.

  2. Add the record at the domain's DNS provider, exactly as dns_records gives it: a CNAME from the domain to the help center's helpcenter.io address.

  3. Follow the checks. HelpCenter.io checks the domain every few minutes and moves it one step at a time: pending_dns, verifying, issuing_certificate, then live. Read GET /v1/custom-domain every five minutes or so. live comes some time after the record appears, not at once.

live means the domain reaches your help center. Readers see its pages there once an Owner or Admin publishes the help center in Settings: until then, every address of it, the custom domain included, answers a page saying it isn't published yet, and only its team can open it.

During the first day after you set a domain, HelpCenter.io looks for its record every few minutes; after that, hourly, and after the first week, daily. Add the record soon after you set the domain. These checks run only for a help center on a trial or on a subscription that hasn't been cancelled; for any other, the domain stays at the step it has reached.

The DNS record

  • A plain CNAME at a subdomain, such as help.acme.example. The API accepts a root domain such as acme.example, but most DNS providers can't put a CNAME there, and a flattened root record shows no CNAME: such a domain stays pending_dns.

  • DNS only, not proxied. A record behind a proxy, such as Cloudflare's orange cloud, hides the CNAME, and the domain never shows as pointing here. See Set up your custom domain with Cloudflare.

  • The name is the full domain. name is help.acme.example; many DNS providers want only the part before your domain (help) in their name field.

  • Keep the record. If it is removed, status goes back to pending_dns.

The custom domain object

Field

Type

Description

domain

string or null

The domain, as stored. null when none is set.

status

string or null

Where the domain stands, in the table below. null when none is set.

dns_records

array

The record to add: one entry with type (CNAME), name (the domain) and value (the help center's helpcenter.io address). Empty when no domain is set.

certificate

object or null

status: waiting_for_dns, requested, issued, not_requested or failed.

checks

object or null

Three booleans: dns_points_here (the domain's CNAME names this help center), certificate_issued and certificate_serving (the certificate serves the domain, checked once the first two are true).

failure

object or null

With status failed: a code and a message to pass on to the help center's owner.

last_checked_at

string or null

When the checks ran, ISO 8601.

The answer doesn't say how many domain changes are left this month.

Statuses

status

Meaning

pending_dns

The domain's CNAME doesn't point at the help center yet, or no longer does.

verifying

The record points here. A next check requests the certificate.

issuing_certificate

The certificate is being issued, or is issued and not serving the domain yet.

live

The record points here, and the certificate is issued and serving the domain.

failed

failure says why.

failure.code

failure.message

domain_in_use

This domain is already connected to another help center. If that help center is yours, remove the domain there first; otherwise contact support.

not_requested

No certificate has been requested for this domain, usually because it was saved after this month's 4 domain changes. Remove it and set it again once the limit allows.

certificate_failed

The certificate for this domain could not be issued. Check the DNS record, then remove the domain and set it again, or contact support.

Setting the same domain again doesn't retry a failed one, because it changes nothing. Follow the message: remove the domain, then set it again, which can count as one of the month's changes.

Get the custom domain

GET/v1/custom-domain

Get the help center's custom domain, the DNS record it needs, and where its checks stand.

Reading never moves a domain along: it reports the latest checks. A read can reuse checks up to a minute old, and last_checked_at says when they ran. The answers to PUT and DELETE always carry fresh checks.

Set the custom domain

Send the domain. The answer is its status, with the record to add.

PUT/v1/custom-domain

Set the help center's custom domain. Setting the domain it already has changes nothing; another domain replaces the current one at once.

Field

Type

Description

domain

string

Required. A host name of up to 253 characters, such as help.acme.example: no https://, path, port or trailing slash. Labels hold letters, digits and hyphens; send an internationalized name in its ASCII (xn--) form.

  • The same domain changes nothing. Setting the domain the help center already has, in any letter case or with a www., answers its status and leaves it as it is, so a retry is safe.

  • Another domain replaces the current one at once. The help center stops answering on the old domain right away, before the new one is live. Replace a live domain only when the help center's owner wants it moved.

  • How it is stored. Every leading www. is removed, in any letter case, so www.help.acme.example is stored as help.acme.example. A www. further in is part of the name and is kept: www.docs.www.acme.example is stored as docs.www.acme.example. Otherwise letter case is kept as you send it: send the domain in lower case.

  • 4 changes in a rolling month. Each new domain you set counts. Setting the domain the help center already has doesn't, and neither does removing it. A fifth change answers 429 Too Many Requests with custom_domain_limit_reached and changes nothing; its Retry-After header gives the seconds until the oldest change in the count is a month old.

The refusals, in the order they are checked:

  • 422 with validation_error for a value that isn't a host name, with Invalid domain name. Please do not include http(s):// or trailing slash. Multiple subdomain levels are supported., and for a helpcenter.io address, with This is not a custom domain: .helpcenter.io addresses are managed automatically. A missing domain answers The domain field is required., and one that isn't text, such as a list, The domain field must be a string. followed by the invalid-name message.

  • 403 with custom_domain_not_in_plan when the help center's plan doesn't include a custom domain: This help center's plan does not include a custom domain. Its owner can upgrade on the billing page. The body's upgrade_url is that billing page.

  • 409 Conflict with custom_domain_busy when another change to this help center's domain, or to the same domain, is still running: Another change to this help center's custom domain, or to this domain, is still running. Retry in a few seconds.

  • 409 with domain_taken when another help center uses the domain. The message names no help center. If the other help center is the owner's, they remove the domain there first.

  • 429 with custom_domain_limit_reached, above.

Remove the custom domain

The help center stops answering on its custom domain at once, and keeps its helpcenter.io address. Removing doesn't count toward the month's changes.

DELETE/v1/custom-domain

Remove the custom domain. With none set, the answer is the same.

With no custom domain set, the answer is the same 200 with "domain": null, so a retry is safe. While another change is running, the answer is 409 with custom_domain_busy.

With an AI agent's key

An AI agent's key reads, sets and removes the custom domain only once the help center it created is claimed and its owner has verified their email address and activated a plan. Until then, every call answers 403 with capability_locked, the conditions it still requires and a next_step to pass on to its user. A key its user lets it keep at the claim holds the custom-domain setting from then on; if the user unticks Allow managing the custom domain for it, all three answer 403 with insufficient_scope. While a migration request holds the help center, setting and removing answer 409 with migration_in_progress. The whole flow is in Migrate a knowledge base as an AI agent.

Errors

Status

Code or message

When

401

"status": "unauthorized"

No key or token, or one the API doesn't accept.

403

insufficient_scope

A key nobody ticked Allow managing the custom domain for, a Read only key, or an OAuth token without domain.manage. A key is told This action requires the domain.manage scope. An API key holds it only when it is a Read & write key and one of the help center's Owners or Admins has ticked "Allow managing the custom domain" for it in Settings > API keys. A token gets the first sentence only.

403

role_forbidden

An OAuth token whose person isn't an Owner or Admin of the help center: Your role on this help center ({role}) does not allow this action, which needs the domain.manage permission there. Ask one of its admins to change your role. The body's role names their role.

403

custom_domain_not_in_plan

Setting a domain on a plan without one. upgrade_url is the billing page.

409

domain_taken

Another help center uses the domain.

409

custom_domain_busy

Another change to the domain is still running. Retry in a few seconds.

422

validation_error

A missing or invalid domain, or a helpcenter.io address.

429

custom_domain_limit_reached

A fifth domain change in a rolling month. Wait Retry-After seconds.

429

{"message": "Too Many Attempts."}

A rate limit. Wait Retry-After seconds.

See Errors for the shapes every endpoint shares, and My custom domain isn't working for DNS problems.

Was this article helpful?