Docs
BlogHomeStart building

Custom Domains

Put your app on your own domain.


A published project answers at <projectId>.totalum-project.com. On the Professional and Enterprise plans it also answers at <projectId>.webapp-project.com — both addresses serve the site — and Update Project { "liveHostSuffix": "webapp-project.com" } makes that one the main address (the one shown as productionProjectUrl and used for links). Add one custom domain per project: a subdomain (app.example.com) or a whole domain (example.com or www.example.com, served as www.example.com; the bare domain redirects to it once it points here, which needs an ALIAS/ANAME record or CNAME flattening at your DNS provider — otherwise add a URL redirect from example.com to https://www.example.com there). You create the DNS records at your DNS provider; SSL is issued automatically. A domain bought on Totalum is attached with its own endpoint and needs no DNS work.

An address without "totalum" in it. For a white-label app, use either option:

  • webapp-project.com (Professional and Enterprise): set liveHostSuffix to "webapp-project.com", and the main address, links, sitemap and preview use <projectId>.webapp-project.com. The totalum-project.com address keeps working too, so old links never break; it just isn't shown anywhere.
  • Your own domain (any paid plan): add a custom domain or buy one on Totalum. It becomes the published address.

Domains are set per project: each project can have its own custom domain and/or use webapp-project.com. One domain that replaces the address of every project of an account at once is not supported yet.

A custom domain costs 4 credits when added and 3 credits a month while active. Custom domains are included on paid plans.

#Add Custom Domain

PUT/api/v1/vcaas/projects/:projectId/domainapi-key4 credits (first domain)

Connect a domain you own. Publish the project first. Then create the records in dnsRecordsToAdd at your DNS provider and poll Get Project until customDomain.status and customDomain.sslStatus are "active" (usually minutes, up to a few hours for DNS). Calling it again with another hostname replaces the domain.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Body parameters

FieldTypeRequiredDescription
hostnamestringYesapp.example.com or example.com

Response fields

FieldTypeDescription
data.successbooleantrue
data.hostnamestringThe hostname that will serve the site
data.statusstring"pending" until the DNS records are found, then "active"
data.dnsRecordsToAddobject[]Records to create: { type: "CNAME" | "TXT" | "ALIAS", name, value }; name is the full host name. For a whole domain the bare domain's ALIAS and TXT come last: optional, they make it redirect to www.

Example request

curl -X PUT \
  -H "api-key: tlm_sk_your_key" \
  -H "Content-Type: application/json" \
  -d '{"hostname":"book.barbershop.com"}' \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/domain
Success · 200 OK
json
{
  "errors": null,
  "data": {
    "success": true,
    "hostname": "book.barbershop.com",
    "status": "pending",
    "dnsRecordsToAdd": [
      { "type": "CNAME", "name": "book.barbershop.com", "value": "barber-bookings.totalum-project.com" },
      { "type": "TXT", "name": "_cf-custom-hostname.book.barbershop.com", "value": "4f6a2c9e-…" }
    ]
  }
}

Error codes

CodeHTTPMeaning
MISSING_HOSTNAME400hostname is missing
VALIDATION_ERROR400Not a valid domain name, or a Totalum host
NO_DEPLOYMENT404Publish the project first
DOMAIN_ALREADY_ADDED409This domain is already on the project
DOMAIN_IN_USE409The domain is connected to another project
PROJECT_HAS_CUSTOM_DOMAIN409The project uses a domain bought on Totalum; detach it first
PLATFORM_CAPABILITY_QUOTA_REACHED403Custom domains are not included on your plan
INSUFFICIENT_CREDITS402Not enough credits
End of Add Custom DomainNext endpointGETGet Custom Domain

#Get Custom Domain

GET/api/v1/vcaas/projects/:projectId/domainapi-keyFree

The project's custom domain and the state of each of its addresses — the site and, for a whole domain, the bare domain that redirects to it. Statuses are checked again with Cloudflare while an address is still pending (at most once a minute), so poll this (or Get Project, whose customDomain carries the same fields) until every status and sslStatus is "active". New engine projects only. data is null when the project has no custom domain.

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.hostnamestringWhere the site answers
data.statusstringThat address: "pending" until its DNS records are found, then "active"
data.sslStatusstringIts HTTPS certificate: "active" once issued
data.domainstringThe domain (barbershop.io)
data.kindstring"domain" or "subdomain"
data.canonicalHoststring | null"www", "app", any label, or "apex"; null for a subdomain
data.sourcestring"purchase" (bought on Totalum: no DNS work) or "manual"
data.accountDomainIdstring | undefinedThe bought domain, when source is "purchase"
data.hostnames[]object[]Each address: { hostname, role, status, sslStatus, dnsRecordsToAdd, verificationErrors }; role is "canonical" (the site), "alias" (redirects to it with a 301, path and query kept) or "sub"
data.dnsRecordsToAddobject[]Every record still to create, as in Add Custom Domain (empty for a bought domain: Totalum writes them)
data.redirectsobject{ canonical, applyToDefaultHost, rules }

Example request

curl -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/domain
Success · 200 OK
json
{ "errors": null, "data": { "hostname": "app.barbershop.io", "status": "active", "sslStatus": "active", "domain": "barbershop.io", "kind": "domain", "canonicalHost": "app", "source": "purchase", "accountDomainId": "dom_01k6y3", "hostnames": [{ "hostname": "app.barbershop.io", "role": "canonical", "status": "active", "sslStatus": "active", "dnsRecordsToAdd": [], "verificationErrors": [] }, { "hostname": "barbershop.io", "role": "alias", "status": "active", "sslStatus": "active", "dnsRecordsToAdd": [], "verificationErrors": [] }], "dnsRecordsToAdd": [], "redirects": { "canonical": "app.barbershop.io", "applyToDefaultHost": false, "rules": [] } } }

#Remove Custom Domain

DELETE/api/v1/vcaas/projects/:projectId/domainapi-keyFree

Disconnect the custom domain. The site keeps answering at <projectId>.totalum-project.com. Your DNS records are not touched. A domain bought on Totalum is detached the same way: it stays yours and is not refunded, and it keeps renewing until you switch autoRenew off (Detach Domain).

Path parameters

ParameterTypeDescription
projectIdstringThe project id

Response fields

FieldTypeDescription
data.successbooleantrue
data.hostnamestring | nullThe domain removed
data.messagestring"Custom domain removed"

Example request

curl -X DELETE -H "api-key: tlm_sk_your_key" \
  https://builder-api.totalum.app/api/v1/vcaas/projects/barber-bookings/domain
Success · 200 OK
json
{ "errors": null, "data": { "success": true, "hostname": "book.barbershop.com", "message": "Custom domain removed" } }
End of Remove Custom Domain Back to top