Skip to content

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:

openshop.d.ts
interface StoreOverview {
name: string;
slug: string;
domain: string;
wideLogoUrl: string;
squareLogoUrl: string;
}
FieldWhat it is
nameThe store’s display name.
slugThe store’s slug.
domainThe domain this store resolves from.
wideLogoUrlURL of the wide (horizontal) logo.
squareLogoUrlURL of the square logo.
app.js
const store = await OpenShop.store.get();
document.title = `${store.name} — Shop`;
document.querySelector('.site-logo').src = store.wideLogoUrl;
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>
MethodWhat 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.
app.js
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.

OpenShop.referral.current: Referral | null | undefined

The attribution Open Shop resolved for this visit — who the shopper is shopping with.

openshop.d.ts
interface Referral {
handle: string;
firstName: string | null;
lastName: string | null;
source: 'HOST' | 'QUERY_PARAM' | 'COOKIE';
}
FieldWhat it is
handleThe enroller’s referral handle.
firstNameThe enroller’s given name, for display.
lastNameThe enroller’s family name, for display.
sourceHow this enroller was resolved. See the values below.

source tells you which attribution path won:

sourceThe shopper…
HOSTarrived on an enroller’s subdomain.
QUERY_PARAMlanded on a page URL that carried ?referral=.
COOKIEwas 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.

Fires when the tracker resolves attribution or clears it. The detail is Referral | null, and it is mirrored on document as os:referral:change.

app.js
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 path must start with a single /. Absolute URLs and protocol-relative // paths are refused with code: 'INVALID_PATH'.
  • JSON in, JSON out. A body is serialised for you. A response that is not JSON rejects with code: '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 OpenShopError carrying the API’s own error code.
app.js
const result = await OpenShop.api.get('/api/v1/some/endpoint');