@joy-loyalty/sdk
v0.3.0
Published
Isomorphic client for the Joy Loyalty Headless API (docs/api/feat-headless-v1.yaml)
Maintainers
Readme
@joy-loyalty/sdk
Isomorphic TypeScript client for the Joy Loyalty Headless API. Use it directly in custom storefronts, or indirectly through @joy-loyalty/hydrogen and @joy-loyalty/weaverse-hydrogen.
ESM-only. Web-standard globals only (
fetch,URL,URLSearchParams), so it runs in browsers, Oxygen workers and React Native.
Prerequisites
A Joy Loyalty store on the Ultimate plan (the Headless API is plan-gated).
Install
npm install @joy-loyalty/sdkEntry points
| Import | Purpose |
|---|---|
| @joy-loyalty/sdk | Shared types and JoyApiError only (no runtime clients). |
| @joy-loyalty/sdk/server | createJoyServerClient — public, shop-scoped GETs for SSR loaders (getPrograms, getTiers, getShopConfig, place-order scope). |
| @joy-loyalty/sdk/client | createJoyClient / Joy — Bearer-JWT /customer/* calls from the browser or native. |
| @joy-loyalty/sdk/ports, /widget-contracts, /loyalty-page-contracts, /headless-loyalty-page-client, /host | Contracts and host plumbing used by @joy-loyalty/hydrogen. Most apps do not need these directly. |
Usage
Server (loader):
import {createJoyServerClient} from '@joy-loyalty/sdk/server';
const joy = createJoyServerClient({
apiUrl: env.JOY_API_URL, // https://api.joy.so/app/api/v1/headless
shopDomain: env.JOY_SHOP_DOMAIN,
locale: storefront.i18n.language.toLowerCase()
});
const [programs, tiers, config] = await Promise.all([joy.getPrograms(), joy.getTiers(), joy.getShopConfig()]);Client (browser/native). The SDK never stores the token; you hand it in and supply a refresher:
import {createJoyClient} from '@joy-loyalty/sdk/client';
const client = createJoyClient({
apiUrl,
shopDomain,
token, // customer JWT, or null for a guest
onTokenExpired: async () => fetchFreshToken() // called once on 401, then the request is retried
});Errors are thrown as JoyApiError.
Obtaining a customer token
Customer JWTs are minted by exchanging a server-signed grant at POST {apiUrl}/auth/token. That requires the headless API secret from your Joy admin, so it must happen on your server (@joy-loyalty/hydrogen/routes/joy-token does this for Hydrogen).
Warning: server-only secret.
JOY_API_SECRETmust only be read on the server. Never prefix itPUBLIC_orVITE_, and never import code that reads it into a client bundle.@joy-loyalty/sdk/clientnever needs the secret. The secret will be rotated to a dedicated headless secret before general availability.
Security notes
The customer JWT is short-lived (about 1 hour). If you keep it in browser storage (as @joy-loyalty/hydrogen does, in sessionStorage), XSS on your origin can read it, so keep a strict CSP.
License
Proprietary. Copyright (c) 2026 Avada Group. All rights reserved. Licensed only for use with the Joy Loyalty service under the Joy Loyalty Terms of Service. See LICENSE.
