Custom Domain Management Feature Context
Source: .agents/references/features/custom-domain-management.md
Content
Custom Domain Management Feature Context
Purpose
Custom domain management lets creators buy, connect, operate, and renew custom domains for their communities while preserving correct public-page, checkout, login, share-link, and NAS-owned settings routing. The domain spans creator portal setup, standalone domain checkout, member-owned domain controls, white-label public routing, and typed custom-domain service contracts.
This context is prompt guidance, not the backend source of truth. For API changes, check Apifox
project 8407323 and inspect the live src/services/customDomain/ contracts before editing.
User-Facing Workflows
- Creator portal setup:
/portal/domainlets eligible creators search for available domains, review suggestions, buy a managed domain, connect an externally owned domain through DNS records, view connection status, disconnect a domain, review billing history, update failed renewal payment, and cancel or undo renewal cancellation. - Standalone checkout:
/purchase-domainhandles custom-domain purchase after portal search. It normalizes query params, confirms the signup/payment flow, supports saved delivery addresses and saved payment methods, handles Stripe/3DS-style payment actions, verifies payment, and routes back to a safe relative destination. - Member-owned domain management:
/user/domainslists domains owned by the current user, lets users connect a domain to an eligible business/community, view billing history, cancel or undo renewals, delete external domains, and open support. - Payment method update:
/user/domains/payment-method-update.page.tsxopens a full-screen update modal from acustomDomainOrderIdand safe relativeredirectTo. - White-label public routing: verified custom domains serve community public pages on slugless URLs, while NAS-only routes redirect back to the NAS host.
Key Entrypoints
- Routes:
src/pages/portal/domain/index.page.tsxsrc/pages/portal/domain/components/PortalDomainPageContent.tsxsrc/pages/portal/domain/components/PortalDomainOwnDomainOverlay.tsxsrc/pages/portal/domain/components/PortalDomainPurchaseCheckoutModal.tsxsrc/pages/purchase-domain/index.page.tsxsrc/pages/purchase-domain/PurchaseDomainPageContent.tsxsrc/pages/purchase-domain/utils.tssrc/pages/user/domains/index.page.tsxsrc/pages/user/domains/components/UserDomainsPageContent.tsxsrc/pages/user/domains/payment-method-update.page.tsx
- Shared custom-domain UI:
src/components/features/CustomDomains/src/components/features/CustomDomains/customDomainBillingHistoryUtils.tssrc/components/features/CustomDomains/types.ts
- Services and types:
src/services/customDomain/customDomainService.tssrc/services/customDomain/types.ts
- Routing, auth, and links:
src/middlewares/whiteLabelDomainRoutingMiddleware.tssrc/middlewares/withMiddlewares.tssrc/contexts/RouterContext/index.tsxsrc/utility/domainUrl.tssrc/utility/crossHostAuthUrl.tssrc/utility/routesHelper.tssrc/utility/domainManagementAccess.ts
- Navigation/config:
src/pages/portal/constants.tsxsrc/pages/user/constants.jssrc/utility/config.jssrc/components/features/Navbar/CopyableBusinessLink.tsxsrc/components/common/PublicPageFooter/index.tsx
Data Flow and Service Boundaries
- Portal and settings navigation visibility can change during rollout. Inspect the live route constants and config before assuming every user can see the domain-management entry points.
CUSTOM_DOMAINinactiveCommunity.permissions.featuresMapgrants custom-domain access whenallowedorisGrandfatheredis true. The legacy domain-management env rollout/whitelist remains a fallback for settings surfaces that cannot resolve a community-scoped permission.- Domain service APIs are community-scoped for creator flows:
GET /api/v1/communities/:communityId/custom-domainsGET /api/v1/communities/:communityId/custom-domains/searchGET /api/v1/communities/:communityId/custom-domains/pricingGET /api/v1/communities/:communityId/custom-domains/:customDomainId/pricingPOST /api/v1/communities/:communityId/custom-domains/signupPOST /api/v1/communities/:communityId/custom-domains/signup/confirmGET /api/v1/communities/:communityId/custom-domains/verify-payment?signupId=...POST /api/v1/communities/:communityId/custom-domains/externalPOST /api/v1/communities/:communityId/custom-domains/:customDomainId/connectGET /api/v1/communities/:communityId/custom-domains/:customDomainId/statusPOST /api/v1/communities/:communityId/custom-domains/:customDomainId/disconnect
- User/domain-owner APIs are global under
/api/v1/custom-domainsfor listing, delete, billing history, receipt generation, renewal cancellation, and payment method updates. signupCustomDomainandconfirmCustomDomainSignuppass{ withSignature: true }; preserve that signature requirement when changing checkout.- Service callers should keep using the repo
{ data, error }response pattern fromadminProtectedAxiosRequest; do not wrap service calls in broadtry/catch. - Domain states include
EMPTY,SEARCHING,CHECKOUT_DRAFT,PAYMENT_PENDING,ACTIVATING,DNS_RECORDS_PENDING,VERCEL_VERIFICATION_PENDING,SSL_PENDING,ACTIVE,DISCONNECTED, andFAILED. UI maps these topending,action-required,connected, andfailed. /purchase-domainaccepts relativebackTo,redirectTo,domain,registrationYear, and active community query state. Helpers reject absolute/external targets, clamp registration years to1..3, normalize domains by stripping protocol/www/path/query, and preserveactiveCommunityIdfor portal return paths.- The purchase signup payload includes
communityId,items,memberInfo,paymentProvider,requestor,timezone, andtrackingData. Registrant contact fields live under each item and includename,phoneNumber,address,unitNo,postalCode,country,countryCode,subdivisionName,subdivisionIsoCode,city,cityGeoNameId, andsaveAddress. - Billing history display is normalized in
customDomainBillingHistoryUtils.ts; keep receipt-link, amount, payment-method, status, and renewal fields tolerant of backend shape variants. - White-label routing uses the community landing-page API, not Edge Config, to resolve both
whiteLabelledDomainandcommunitySluglookups.
Conventions and Gotchas
- Custom-domain checkout and settings execute on the NAS host.
NAS_ONLY_CUSTOM_DOMAIN_PATHSinsrc/utility/domainUrl.tsincludes/portal,/user,/checkout-global, payment/auth routes, plan routes, and setup routes. Keep this list aligned with middleware and router behavior. - Active ZeroLink pages can start checkout from a supported custom domain. Resolve their relative
/checkout-globalroute withresolveNavigationHrefbefore the forced hard navigation; do not rely on an intermediate custom-host request reaching the Vercel redirect, because WebKit can cancel that request and reload the source page. - Custom-domain public paths are slugless. A NAS path like
/a/courses/course-idcan redirect tohttps://custom-domain.com/courses/course-id; a custom-domain path like/a/homeshould redirect to/. - Cross-host login/share continuity relies on
buildCrossHostAuthUrland middleware token cleanup. Only appendaccessTokenandrefreshTokento trusted cross-host targets, then remove token params after preserving the normal middleware redirect/rewrite decision. - Locale redirects must run before custom-domain rewrites. Changing middleware order can break custom domains, localized public pages, or auth-token cleanup.
- Search UX should keep exact domain matches first and separate smart-pick/suggestion results. The
portal modal defaults to a two-year registration period, while utility guards still support
1..3. - Renewal states are nuanced: auto-renewal-off, renewal cancellation pending, failed renewal payment, terminated/grace-period states, and active/connecting states render different rows and available actions.
- External/own-domain flows use DNS records and status polling. Preserve DNS record copy behavior and
the
DNS_RECORDS_PENDING/ verification-pending / SSL-pending transitions. - User-domain rows distinguish externally owned domains from purchased managed domains; deleting an external domain is not the same operation as disconnecting a community domain or canceling renewal.
- Avoid direct
window/documentuse except guarded browser-only code. Route utilities and middleware can run server-side. - Do not add broad locale-file churn. Use direct
t('key')calls and add only missing focused English keys when implementation work requires new copy.
Related Standards
.agents/rules/apifox-api-source-of-truth.mdc.agents/rules/no-try-catch-service-api.mdc.agents/rules/web-security.mdc.agents/rules/clean-typescript.mdc.agents/rules/no-type-assertion-as.mdc.agents/rules/clean-react.mdc.agents/rules/page-structure.mdc.agents/references/features/auth-routing-and-middleware.md.agents/references/features/checkout-and-payments.md.agents/references/features/community-public-pages.md.agents/references/features/creator-admin-and-pro.md
Useful Graph Queries and Fallback Searches
Use the terms below with search_graph/search_code, then run trace_path on central domain-management
and routing symbols. Run these shell searches only as documented fallbacks.
rtk rg "customDomain|custom-domain|whiteLabelledDomain|purchase-domain|portal/domain|user/domains" src
rtk rg "CMP_PORTAL_DOMAIN_PAGE_ROUTE|USER_DOMAINS_ROUTE|PURCHASE_DOMAIN_ROUTE" src
rtk rg "signupCustomDomain|confirmCustomDomainSignup|verifyCustomDomainPayment|getCustomDomainBillingHistory|cancelCustomDomainRenewal|updateCustomDomainPaymentMethod" src
rtk find src/pages/portal/domain src/pages/purchase-domain src/pages/user/domains src/components/features/CustomDomains src/services/customDomain -maxdepth 4 -type f
Update Triggers
Update this reference when a change adds, removes, or renames a custom-domain route, service endpoint,
state mapping, billing/renewal behavior, white-label route policy, cross-host auth/share behavior,
domain-management route visibility, or major shared component under src/components/features/CustomDomains/.