Skip to content

Customer

OpenShop.customer tells you whether a shopper is signed in and, if they are, who they are. It is the supported answer to “show the customer’s name in the nav” and “swap the Login link for an Account link”.

openshop.d.ts
interface Customer {
id: number;
firstName: string | null;
lastName: string | null;
accountTypeName: string | null;
priceTypeName: string;
isAffiliate: boolean;
householdRole: 'PRIMARY' | 'MEMBER' | null;
}
FieldTypeWhat it is
idnumberThe customer’s Open Shop id.
firstNamestring | nullGiven name, as the shopper entered it.
lastNamestring | nullFamily name, as the shopper entered it.
accountTypeNamestring | nullThe account type this shopper holds in your store.
priceTypeNamestringThe price type driving what they pay.
isAffiliatebooleanWhether the shopper is an affiliate.
householdRole'PRIMARY' | 'MEMBER' | nullTheir role in a household, or null when they are not in one.

The snapshot is frozen and replaced wholesale on every change. It is a curated view, not the raw API response — see the note on email and phone below.

OpenShop.customer.current: Customer | null | undefined
  • undefined — login state has not resolved yet.
  • null — resolved, and the shopper is a guest.
  • A Customer — resolved, and they are signed in.

Read it whenever you need the current answer, and listen for customer:change to be told when it changes.

app.js
OpenShop.on('customer:change', (customer) => {
if (customer === null) return showLoginLink();
showGreeting(customer.firstName);
});
OpenShop.customer.get(options?: { refresh?: boolean }): Promise<Customer | null>

Resolves with the current snapshot, waiting for the in-flight page-load request if it has not finished yet. Pass { refresh: true } to force a fresh request to the API — this is the retry path, and it fires customer:change when it lands.

app.js
const customer = await OpenShop.customer.get();
if (customer) {
console.log('Signed in as', customer.firstName, customer.lastName);
}
// After something that may have changed the session
const confirmed = await OpenShop.customer.get({ refresh: true });
OpenShop.customer.handoffToken(): Promise<string | null>

Mints a short-lived token you can hand to another app on the same account, so a signed-in shopper is not asked to log in twice.

It never rejects — every unhappy path resolves to null, so a single if covers all of them. A shopper already known to be a guest short-circuits to null without a request. If login state has not resolved yet the request is made anyway, and a guest’s 401 resolves to null just the same.

app.js
const token = await OpenShop.customer.handoffToken();
if (token) {
window.location.href = `/assessment/?token=${encodeURIComponent(token)}`;
} else {
window.location.href = '/assessment/';
}

If all you want is a link that does this, use the data-os-handoff attribute instead — no script required.

OpenShop.customer.logout(): Promise<void>

Ends the session, then publishes customer:change with null, refreshes the cart, and announces the change to your other open tabs.

app.js
document.querySelector('.nav-logout').addEventListener('click', async (event) => {
event.preventDefault();
await OpenShop.customer.logout();
window.location.href = '/';
});

There is no matching login() method, and there will not be one. Signing in stays on the Open Shop /customer page so a password never passes through your site’s custom code.

Open Shop’s session cookies are httpOnly, so JavaScript cannot read them. The bundle has to ask the server, and it does so like this:

  1. One request per page load. On init the bundle calls GET /customer/me once, in parallel with the cart summary. A 200 becomes the snapshot; a 401 becomes null; a network failure also becomes null, plus a customer:change so your UI settles rather than hanging on undefined.
  2. No automatic retry. The bundle does not retry on its own. get({ refresh: true }) is the retry.
  3. Cached optimistic paint. The last resolved snapshot is kept in sessionStorage and published as current immediately on the next page, before the request comes back. That is what stops the “guest, then their name” flicker on every navigation. The request then confirms or corrects it, and a 401 clears the cache.
  4. Cross-tab. Logging out in one tab is announced to your other open tabs on the shared os-cart broadcast channel, and each one re-resolves.

Because the first paint of a repeat page view can come from the cache, treat current as the best answer so far and let customer:change correct you.