Skip to content

Tenant Isolation & Scope

Tenant isolation is the central contract of the CDP API. The CDP runs on one shared Unomi cluster across every brand, so isolation is enforced in software on every request, not by physical separation.

When a read request arrives:

  1. The credential is resolved to an authenticated context (a brand_id).
  2. The brand’s shop domain is resolved from its connected Shopify integration (either the LiveRecover integration id or the CDP-app integration id).
  3. A scope-bound client is constructed for that shop domain, and the handler is invoked with only that client.

The scope is always the one resolved from your credential — never anything you can pass in. A handler structurally cannot issue an unscoped read: the raw, unscoped client is never reachable from request code. Every response echoes the resolved scope so you can confirm which shop a read was bound to.

  • A key for shop A can never read shop B’s data. Every profile / event / session / segment read AND-injects the shop-scope predicate; cross-shop rows are never returned.
  • Single-profile reads fail closed. Requesting a profile id that belongs to another brand returns the same 404 as a non-existent id — a brand cannot even probe for the existence of another brand’s ids.
  • Merged profiles only surface your data. A profile merged across shops only ever surfaces this brand’s events and sessions; a merged master is labelled merged, never silently presented as one confirmed human.
  • Segments cannot span brands. Segment membership cannot span shops (the scope predicate is AND-ed into every condition), and segments are visible only to the owning brand.
  • Cursors are scope-bound. A deep-pagination cursor embeds the scope it was minted under; presenting another brand’s cursor is rejected (403) and never used to select a scope. See Pagination.

The credential-free ingest proxy binds every payload scope to the requesting storefront Origin: a script on storefront A cannot post events tagged with shop B’s scope. Every tenancy-bearing scope in the payload must equal the shop bound to the request Origin, or the request is rejected (403). See Ingest.