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”.
The Customer snapshot
Section titled “The Customer snapshot”interface Customer { id: number; firstName: string | null; lastName: string | null; accountTypeName: string | null; priceTypeName: string; isAffiliate: boolean; householdRole: 'PRIMARY' | 'MEMBER' | null;}| Field | Type | What it is |
|---|---|---|
id | number | The customer’s Open Shop id. |
firstName | string | null | Given name, as the shopper entered it. |
lastName | string | null | Family name, as the shopper entered it. |
accountTypeName | string | null | The account type this shopper holds in your store. |
priceTypeName | string | The price type driving what they pay. |
isAffiliate | boolean | Whether the shopper is an affiliate. |
householdRole | 'PRIMARY' | 'MEMBER' | null | Their 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.
current
Section titled “current”OpenShop.customer.current: Customer | null | undefinedundefined— 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.
OpenShop.on('customer:change', (customer) => { if (customer === null) return showLoginLink(); showGreeting(customer.firstName);});get(options?)
Section titled “get(options?)”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.
const customer = await OpenShop.customer.get();
if (customer) { console.log('Signed in as', customer.firstName, customer.lastName);}
// After something that may have changed the sessionconst confirmed = await OpenShop.customer.get({ refresh: true });handoffToken()
Section titled “handoffToken()”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.
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.
logout()
Section titled “logout()”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.
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.
How login state is resolved
Section titled “How login state is resolved”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:
- One request per page load. On init the bundle calls
GET /customer/meonce, in parallel with the cart summary. A200becomes the snapshot; a401becomesnull; a network failure also becomesnull, plus acustomer:changeso your UI settles rather than hanging onundefined. - No automatic retry. The bundle does not retry on its own.
get({ refresh: true })is the retry. - Cached optimistic paint. The last resolved snapshot is kept in
sessionStorageand published ascurrentimmediately 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 a401clears the cache. - Cross-tab. Logging out in one tab is announced to your other open tabs on the shared
os-cartbroadcast 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.
Related
Section titled “Related”- Data attributes —
data-os-show="customer"anddata-os-text="customer.firstName"do the common cases without JavaScript. - Webflow tutorial, Step 6 — a walkthrough for a Webflow nav.