Skip to main content

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/domain lets 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-domain handles 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/domains lists 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.tsx opens a full-screen update modal from a customDomainOrderId and safe relative redirectTo.
  • 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.tsx
    • src/pages/portal/domain/components/PortalDomainPageContent.tsx
    • src/pages/portal/domain/components/PortalDomainOwnDomainOverlay.tsx
    • src/pages/portal/domain/components/PortalDomainPurchaseCheckoutModal.tsx
    • src/pages/purchase-domain/index.page.tsx
    • src/pages/purchase-domain/PurchaseDomainPageContent.tsx
    • src/pages/purchase-domain/utils.ts
    • src/pages/user/domains/index.page.tsx
    • src/pages/user/domains/components/UserDomainsPageContent.tsx
    • src/pages/user/domains/payment-method-update.page.tsx
  • Shared custom-domain UI:
    • src/components/features/CustomDomains/
    • src/components/features/CustomDomains/customDomainBillingHistoryUtils.ts
    • src/components/features/CustomDomains/types.ts
  • Services and types:
    • src/services/customDomain/customDomainService.ts
    • src/services/customDomain/types.ts
  • Routing, auth, and links:
    • src/middlewares/whiteLabelDomainRoutingMiddleware.ts
    • src/middlewares/withMiddlewares.ts
    • src/contexts/RouterContext/index.tsx
    • src/utility/domainUrl.ts
    • src/utility/crossHostAuthUrl.ts
    • src/utility/routesHelper.ts
    • src/utility/domainManagementAccess.ts
  • Navigation/config:
    • src/pages/portal/constants.tsx
    • src/pages/user/constants.js
    • src/utility/config.js
    • src/components/features/Navbar/CopyableBusinessLink.tsx
    • src/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_DOMAIN in activeCommunity.permissions.featuresMap grants custom-domain access when allowed or isGrandfathered is 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-domains
    • GET /api/v1/communities/:communityId/custom-domains/search
    • GET /api/v1/communities/:communityId/custom-domains/pricing
    • GET /api/v1/communities/:communityId/custom-domains/:customDomainId/pricing
    • POST /api/v1/communities/:communityId/custom-domains/signup
    • POST /api/v1/communities/:communityId/custom-domains/signup/confirm
    • GET /api/v1/communities/:communityId/custom-domains/verify-payment?signupId=...
    • POST /api/v1/communities/:communityId/custom-domains/external
    • POST /api/v1/communities/:communityId/custom-domains/:customDomainId/connect
    • GET /api/v1/communities/:communityId/custom-domains/:customDomainId/status
    • POST /api/v1/communities/:communityId/custom-domains/:customDomainId/disconnect
  • User/domain-owner APIs are global under /api/v1/custom-domains for listing, delete, billing history, receipt generation, renewal cancellation, and payment method updates.
  • signupCustomDomain and confirmCustomDomainSignup pass { withSignature: true }; preserve that signature requirement when changing checkout.
  • Service callers should keep using the repo { data, error } response pattern from adminProtectedAxiosRequest; do not wrap service calls in broad try/catch.
  • Domain states include EMPTY, SEARCHING, CHECKOUT_DRAFT, PAYMENT_PENDING, ACTIVATING, DNS_RECORDS_PENDING, VERCEL_VERIFICATION_PENDING, SSL_PENDING, ACTIVE, DISCONNECTED, and FAILED. UI maps these to pending, action-required, connected, and failed.
  • /purchase-domain accepts relative backTo, redirectTo, domain, registrationYear, and active community query state. Helpers reject absolute/external targets, clamp registration years to 1..3, normalize domains by stripping protocol/www/path/query, and preserve activeCommunityId for portal return paths.
  • The purchase signup payload includes communityId, items, memberInfo, paymentProvider, requestor, timezone, and trackingData. Registrant contact fields live under each item and include name, phoneNumber, address, unitNo, postalCode, country, countryCode, subdivisionName, subdivisionIsoCode, city, cityGeoNameId, and saveAddress.
  • 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 whiteLabelledDomain and communitySlug lookups.

Conventions and Gotchas

  • Custom-domain checkout and settings execute on the NAS host. NAS_ONLY_CUSTOM_DOMAIN_PATHS in src/utility/domainUrl.ts includes /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-global route with resolveNavigationHref before 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-id can redirect to https://custom-domain.com/courses/course-id; a custom-domain path like /a/home should redirect to /.
  • Cross-host login/share continuity relies on buildCrossHostAuthUrl and middleware token cleanup. Only append accessToken and refreshToken to 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/document use 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.
  • .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/.