Store, Products, Referral
The remaining namespaces on window.OpenShop: store settings, product data, referral attribution, and a thin escape hatch for endpoints the API surface above does not cover.
OpenShop.store.get(): Promise<StoreOverview>Resolves with the overview of the store this domain belongs to. The bundle requests it during init and keeps the result in memory, so by the time your code runs this usually resolves from what is already there.
StoreOverview is a curated shape — a store’s name, its identifiers, and its two logos. It is not a settings object:
interface StoreOverview { name: string; slug: string; domain: string; wideLogoUrl: string; squareLogoUrl: string;}| Field | What it is |
|---|---|
name | The store’s display name. |
slug | The store’s slug. |
domain | The domain this store resolves from. |
wideLogoUrl | URL of the wide (horizontal) logo. |
squareLogoUrl | URL of the square logo. |
const store = await OpenShop.store.get();document.title = `${store.name} — Shop`;document.querySelector('.site-logo').src = store.wideLogoUrl;products
Section titled “products”OpenShop.products.card(slug: string): Promise<ProductCard>OpenShop.products.details(slug: string): Promise<ProductDetails>OpenShop.products.collections(): Promise<Collection[]>OpenShop.products.paged(query: PagedQuery): Promise<PagedProducts>| Method | What it returns |
|---|---|
card(slug) | The compact card view of one product — what os-single-product renders. |
details(slug) | The full detail view of one product — what os-product-details renders. |
collections() | Every collection in the store. |
paged(query) | A page of products, for building your own grid or search. |
const product = await OpenShop.products.card('awesome-cream');await OpenShop.cart.add({ productId: product.id });These are the same calls the product components make, so what you get back matches what the components on the page are showing.
Failures reject with an OpenShopError carrying the HTTP status and a code derived from it — a product that does not exist rejects with code: 'NOT_FOUND' and status: 404. These calls do not read the API’s own err code out of the body, so branch on status here; status: 0 still means the request never got an answer. cart.add() and api.* are where the backend’s own codes come through.
The returned shapes are declared in openshop.d.ts; the full field-by-field reference lives in the API docs.
referral
Section titled “referral”OpenShop.referral.current: Referral | null | undefinedThe attribution Open Shop resolved for this visit — who the shopper is shopping with.
interface Referral { handle: string; firstName: string | null; lastName: string | null; source: 'HOST' | 'QUERY_PARAM' | 'COOKIE';}| Field | What it is |
|---|---|
handle | The enroller’s referral handle. |
firstName | The enroller’s given name, for display. |
lastName | The enroller’s family name, for display. |
source | How this enroller was resolved. See the values below. |
source tells you which attribution path won:
source | The shopper… |
|---|---|
HOST | arrived on an enroller’s subdomain. |
QUERY_PARAM | landed on a page URL that carried ?referral=. |
COOKIE | was remembered from an earlier visit. |
undefined means attribution has not resolved yet; null means it resolved to nobody. The os-referral-tracker component is what resolves it, so keep that component on the page.
referral:change
Section titled “referral:change”Fires when the tracker resolves attribution or clears it. The detail is Referral | null, and it is mirrored on document as os:referral:change.
OpenShop.on('referral:change', (referral) => { const label = document.querySelector('.shopping-with'); label.hidden = referral === null; if (referral) label.textContent = `Shopping with ${referral.firstName}`;});There is no setter. Which enroller wins, and whether a shopper picked one by hand, is decided inside Open Shop and is not something site code can override.
The data-os-show="referral" and data-os-text="referral.firstName" attributes cover the display case without a script.
OpenShop.api.get<T>(path: string): Promise<T>OpenShop.api.post<T>(path: string, body?: unknown): Promise<T>An escape hatch for endpoints the namespaces above do not wrap. Both are thin:
- Same-origin only. The
pathmust start with a single/. Absolute URLs and protocol-relative//paths are refused withcode: 'INVALID_PATH'. - JSON in, JSON out. A
bodyis serialised for you. A response that is not JSON rejects withcode: 'INVALID_RESPONSE'. - The
{ data }envelope is unwrapped, so you get the payload directly. - No headers are added. The browser sends the session cookies; nothing else is needed.
- Non-2xx rejects with an
OpenShopErrorcarrying the API’s own error code.
const result = await OpenShop.api.get('/api/v1/some/endpoint');