Storefront-ID multi-domain analytics — how 13 marketing sites measure under one tenant
A classic dilemma: you run 13 marketing domains (each one a separate sub-brand, audience, campaign), and you have to decide how to measure them. The “trivial” answer: 13 GA4 properties + 13 GTM containers. We don't do it that way — and it's worth saying why.
The anti-pattern: 13 separate analytics properties
Why it looks good at first:
- Each site is managed separately, separate user list, separate goal set.
- Marketing teams can build per-site dashboards.
- The “small Acme” domain doesn't muddy “big nortinia.com”.
Why it doesn't hold up:
- Cross-domain user journeys can't be measured. The buyer lands via a campaign page (acme-landing.com), clicks through to the main brand (nortinia.com), buys there. In GA4 that's two separate sessions, and conversion attribution falls apart.
- 13× maintenance. Introducing one new event (e.g.
cart-line-clicked-thumbnail) means 13 places, 13 morning sync meetings. - No aggregate reporting. “What's total conversion across the ecosystem?” — the CEO question is only answerable in Excel.
- Tenant-level AI models (recommender, predictor) starve. Not enough samples per site, and the 13 sample pools can't be combined.
The Nortinia approach: X-Storefront-Domain header + one pool
Every ecosystem site calls the same backend, with each request including an X-Storefront-Domain: <host> header. The analytics ingest writes into a single store where storefront_id is a dimension.
GET /api/v1/public/products HTTP/1.1
Host: api.nortinia.com
X-Storefront-Domain: acme-landing.com
The dashboard then offers:
- Default view — all sites combined (CEO dashboard, ecosystem-level KPIs)
- Per-storefront filter — one click in the sidebar; marketing teams scope to their own site
- Cross-domain funnel —
storefront_id IN (acme-landing.com, nortinia.com)→ the real journey
What we gain, concretely
A Q1 2026 audit on our own ecosystem:
- Attribution lift: 23% of cross-domain journeys originated externally but converted on the main domain. Previously the main domain looked like first touch; really the acme-landing was the actual acquisition channel. The 4,200 EUR/month ad budget there got reweighted.
- New event rollout: time-to-ship for
bundle-suggestion-showndropped from 13 sprints to 1. Implement once in the shared package. - AI recommender training time: 6 weeks → 9 days, because aggregate data from every site lands in one pool.
The honest trade-offs
No free lunch:
- Tenant separation in the backend is rigorous —
tenant_idon every query, RLS on, audit log on per-storefront filter usage. One missed filter → cross-leak. - GDPR consent stored per storefront — consent given on
acme-landing.comdoes NOT carry tonortinia.com. Separate cookie banner, separate consent record, keyed by(user_hash, storefront_id, consent_type). - Higher learning curve for marketers — they have to learn filtering to get the per-site view. By default it's not their site showing up; that's frustrating at first.
When NOT to do this
If the 13 sites are:
- separate legal entities (your B2B partners, not your ecosystem),
- separate GDPR data controllers,
- never going to have a cross-domain user journey,
…then 13 separate properties isn't just acceptable — it's required. nortinia.com / netorigo.com / 11 sibling sites are one ecosystem, one tenant, one data controller — single-pool is the natural answer there.
Takeaway
“13 sites → 13 analytics” looks trivial, but it loses what hinges on the number 13: the journeys. If your buyers cross domains, measure with a storefront_id dimension in a single pool — and plan separation duties into the backend and consent layer.