Summary: The elixir-backend serves both the 7Mind and 7Sleep products from a single codebase and deployment, using request-time routing to give each brand its own API surface and domain logic. Sources: direct code inspection (lib/backend/web/, config/runtime/prod.ex) Last updated: 2026-05-15
The problem it solves
7Mind (meditation app) and 7Sleep (sleep app) share most infrastructure: authentication, billing, content delivery, practice tracking. Maintaining two separate backends would duplicate this. Instead, one backend handles both, with routing logic deciding which brand context a given request operates in.
How routing works
The Public API endpoint (port 4100) inspects incoming requests to determine brand context:
- By hostname:
7mind.7mind.deroutes toSevenMindRouter;api.7sleep.deroutes toSevenSleepRouter - By header: the
x-appheader can explicitly declare the brand
Both routers expose the same route namespaces (/practice/*, /content/*, /user/*, etc.) but may have brand-specific implementations behind them.
Chargebee billing is configured with separate namespaces and API keys per brand (SEVENMIND_CHARGEBEE_NAMESPACE, SLEEP_CHARGEBEE_NAMESPACE). Rudderstack analytics uses separate write keys per brand. Braze uses separate app IDs. Each brand’s data is isolated at the service-integration level even though the Elixir code is shared.
Content Provider pattern
The content domain uses a Provider behaviour (Content.SevenSleep.Provider / Content.SevenMind.Provider) to abstract CMS differences between the two apps. Brand-specific content configuration is injected at runtime, not compiled in.
What agents should know
- When adding a feature that behaves differently per brand, use the existing routing layer and Provider pattern rather than introducing a new branching mechanism.
- Env vars for external services come in pairs — one per brand. Always check both when debugging billing, analytics, or content issues.
- The admin panel (port 4200) is brand-agnostic; it operates across both brands.
- Mobile clients identify themselves — agents editing API handlers should assume either brand can call any shared endpoint.
From 7mind-mobile-apps-monorepo
Despite the plural “mobile-apps-monorepo” name, the Flutter codebase only ships the 7Mind brand. There is no 7Sleep app code in this repo. As a consequence:
- The
x-appheader sent by mobile clients only ever identifies 7Mind from this repo - 7Sleep mobile traffic, if it exists, must come from a different repository or be web-only
- Per-brand mobile configuration (Chargebee namespace, Rudderstack write key, Braze app ID) is single-valued, not selected at runtime
- Future agents should not assume a “7Sleep flavor” can be added without significant rewiring (new flavors, new Firebase project, new SSO client IDs, new push certificates)
From nuxt-website
The web does not use request-time brand routing. It uses build-time multiplexing via a THEME env var: one codebase produces two distinct Docker images, one per brand. See theme-build-multiplexing for the full pattern. Implications for backend agents:
- The 7mind web ships with the SevenMind brand baked in; 7sleep web ships separately. Each calls
magic.7mind.deormagic.7sleep.derespectively, so the backend’s hostname-based routing kicks in naturally. - The web does not send an
x-appheader. Brand selection on the backend for web requests is hostname-driven. - Web requests can come from
7mind.de,7sleep.de, plus localized TLDs (.fr,.it,.nl,.es,.uk,.ca, etc.) and migration / preview hosts (6mind.de,8mind.de,*.review.6mind.de). All of these resolve to the same brand pair (7mindfamily → SevenMind,7sleepfamily → SevenSleep). The backend’s router needs to recognize the whole family, not just the canonical.dehost.
Related
- modulith-domain-model — how domain isolation is maintained despite shared code
- chargebee-billing — billing is the most significant per-brand configuration difference
- elixir-backend — the repo this pattern lives in
- 7mind-mobile-apps-monorepo — the mobile client that sends the brand context header (7Mind only)
- nuxt-website — the web client; uses theme-build-multiplexing instead of request-time brand routing
- theme-build-multiplexing — the web’s compile-time equivalent of this pattern