codeDeveloper Docs

Headless SDK

Add the full Anchor loyalty widget to any headless storefront — Hydrogen, Next.js, Gatsby, Vue, or custom builds.

widgetsFull widget experience
memoryLightweight footprint
deployed_codeFramework-agnostic
workspace_premiumThe Headless SDK is available on the Pro plan ($39.99/mo)

#Overview

The Anchor Loyalty Headless SDK allows you to add the loyalty widget to any storefront that doesn't use Shopify's Online Store channel — including Hydrogen, Next.js, Gatsby, Vue, or any custom frontend.

The SDK renders the exact same widget as the embedded version — same UI, same features, lightweight footprint. No custom UI development required.

infoHow it works

The SDK handles all communication with Anchor securely. You only need to provide your API key during initialization — the SDK takes care of the rest.

  • checkAnchor Loyalty app installed on your Shopify store
  • checkPRO plan subscription
  • checkAPI key generated from the Anchor Loyalty dashboard

#Authentication

All headless SDK API calls require an API key.

  1. 1Generate your API keyfrom the Anchor Loyalty dashboard (Headless SDK section).
  2. 2Save the key securelyit is displayed only once. If you lose it, use "Regenerate Key" to create a new one (this invalidates the old key).
  3. 3Include apiKey in your SDK initializationcall (see Quick Start below).
  4. 4Revoke and regenerateif a key is compromised, revoke it from the dashboard. A new key can be generated immediately.
  5. 5Generate customer tokensYour backend must sign a customer token using HMAC-SHA256 with your signing secret. The signing secret is shown alongside the API key when generated — store it securely in your backend environment variables. Required for all authenticated requests (reads and redemptions). See Security section.

#Security — Customer Token

The API key authenticates your store, but it's visible in your page source. To prevent unauthorized access, all authenticated headless requests require a customer token — an HMAC-signed proof that your backend verified the customer.

#How it works

  1. 1Customer logs inon your storefront.
  2. 2Your backend generates a signed tokenusing HMAC-SHA256 with your signing secret (never send the signing secret to the browser).
  3. 3Frontend passes the tokento the SDK via the customerToken parameter.
  4. 4Anchor verifies the signaturebefore processing any request.

#Generating the token (backend)

descriptionNode.jsJS
const crypto = require('crypto');
const ts = Math.floor(Date.now() / 1000);
const hmac = crypto.createHmac('sha256', SIGNING_SECRET)
  .update(`${customerId}.${ts}`).digest('hex');
const customerToken = `${customerId}.${ts}.${hmac}`;
descriptionHydrogen loaderTSX
// In a Hydrogen loader (e.g., app/routes/($locale)._index.tsx)
import crypto from 'crypto';

export async function loader({ context }: LoaderFunctionArgs) {
  const isLoggedIn = await context.customerAccount.isLoggedIn();
  let customerToken = null;

  if (isLoggedIn) {
    const { data } = await context.customerAccount.query(`
      query { customer { id } }
    `);
    const customerId = data.customer.id.replace('gid://shopify/Customer/', '');
    const ts = Math.floor(Date.now() / 1000);
    const hmac = crypto.createHmac('sha256', context.env.ANCHOR_SIGNING_SECRET)
      .update(`${customerId}.${ts}`).digest('hex');
    customerToken = `${customerId}.${ts}.${hmac}`;
  }

  return json({ customerToken });
}

Token expires after 5 minutes. Regenerate on each page load.

#When is it required?

EndpointTokenNote
POST /api/redeemREQUIREDReturns 403 without valid token
GET /api/customerREQUIREDReturns 403 without valid token
GET /api/codesREQUIREDReturns 403 without valid token

#Quick Start

#HTML / Custom Storefront

Recommended — only API key needed:

descriptionindex.html — recommendedHTML
<script src="https://anchorloyalty.app/api/sdk.js?key=YOUR_API_KEY"></script>
<script>
  AnchorLoyaltySDK.init({
    apiKey: 'YOUR_API_KEY',
    customerId: '12345', // Shopify Customer ID (numeric)
    customerToken: 'TOKEN_FROM_YOUR_BACKEND', // HMAC-signed token
  });
</script>

When loading via ?key=, the SDK automatically resolves your store — no shop parameter needed. Your myshopify.com domain is never exposed in page source.

Alternative — with explicit shop domain:

descriptionindex.html — alternativeHTML
<script src="https://anchorloyalty.app/api/sdk.js?shop=YOUR-STORE.myshopify.com"></script>
<script>
  AnchorLoyaltySDK.init({
    shop: 'YOUR-STORE.myshopify.com',
    apiKey: 'YOUR_API_KEY',
    customerId: '12345',
  });
</script>

#Hydrogen / React

Create a LoyaltyWidget component that loads the SDK and initializes it with the authenticated customer:

descriptionLoyaltyWidget.tsxTSX
import { useEffect } from 'react';
import { useCustomer } from '~/hooks/useCustomer';

const API_KEY = import.meta.env.PUBLIC_ANCHOR_LOYALTY_API_KEY;

export function LoyaltyWidget() {
  const customer = useCustomer();

  useEffect(() => {
    if (!API_KEY) {
      console.error('Missing PUBLIC_ANCHOR_LOYALTY_API_KEY env var');
      return;
    }
    const script = document.createElement('script');
    script.src = `https://anchorloyalty.app/api/sdk.js?key=${API_KEY}`;
    script.async = true;
    script.onload = () => {
      window.AnchorLoyaltySDK?.init({
        apiKey: API_KEY,
        customerId: customer?.id?.replace('gid://shopify/Customer/', '') || null,
        customerToken: customerToken, // Pass token from your backend
        customerName: customer?.firstName || '',
        locale: 'en',
      });
    };
    document.body.appendChild(script);

    return () => {
      window.AnchorLoyaltySDK?.destroy();
      script.remove();
    };
  }, [customer]);

  return null;
}

#Next.js

For Next.js, use the built-in next/script component with the onReady callback. This ensures the SDK re-initializes on SPA route transitions:

descriptionLoyaltyWidget.tsxTSX
'use client';
import Script from 'next/script';

const API_KEY = process.env.NEXT_PUBLIC_ANCHOR_LOYALTY_API_KEY;

export function LoyaltyWidget({ customerId, customerName, customerToken }: {
  customerId: string | null;
  customerName?: string;
  customerToken?: string;
}) {
  if (!API_KEY) {
    console.error('Missing NEXT_PUBLIC_ANCHOR_LOYALTY_API_KEY env var');
    return null;
  }
  return (
    <Script
      src={`https://anchorloyalty.app/api/sdk.js?key=${API_KEY}`}
      strategy="lazyOnload"
      onReady={() => {
        window.AnchorLoyaltySDK?.init({
          apiKey: API_KEY,
          customerId,
          customerName,
          customerToken,
        });
      }}
    />
  );
}

Why next/script? It handles deduplication (loads only once across navigations), supports lazyOnload for zero performance impact, and onReady re-fires on every mount — perfect for SPA route changes.

#Getting the Customer ID

The customerId must be the Shopify numeric Customer ID (not the GID).

#From Hydrogen (Customer Account API)

descriptionHydrogen loaderTSX
// In a Hydrogen loader:
const isLoggedIn = await context.customerAccount.isLoggedIn();
if (isLoggedIn) {
  const { data } = await context.customerAccount.query(`
    query { customer { id firstName lastName } }
  `);
  // data.customer.id = "gid://shopify/Customer/12345"
  const numericId = data.customer.id.replace('gid://shopify/Customer/', '');
  // numericId = "12345"
}

#From Storefront API

descriptionStorefront API QueryGraphQL
query {
  customer(customerAccessToken: "TOKEN") {
    id  # Returns "gid://shopify/Customer/12345"
  }
}

Extract the numeric part: id.split('/').pop()"12345"

#From Custom Backend (Admin API)

descriptionAdmin APIHTTP
GET /admin/api/2026-01/customers.json?email=user@example.com

Use the id field from the response (already numeric).

#Guest Users (Not Logged In)

Pass null or omit customerId:

descriptionGuest ModeJS
AnchorLoyaltySDK.init({
  apiKey: 'YOUR_API_KEY',
  customerId: null, // Guest mode — shows sign-in prompt
});

#All Parameters

ParameterTypeDefaultDescription
apiKeystringrequiredAPI key from your Anchor Loyalty dashboard
shopstringauto-resolvedYour *.myshopify.com domain. Auto-resolved from API key when SDK is loaded via ?key=
customerIdstringnullShopify Customer ID (numeric). Omit or pass null for guest mode
customerTokenstringnullHMAC-signed customer verification token. Required for all authenticated requests (reads and redemptions). Generated by your backend: HMAC-SHA256(customerId.timestamp, signingSecret). See Security section below.
customerNamestring''Customer's first name for welcome message
localestring'en'Language code. 32 locales built-in (see below)
positionstring'bottom-right''bottom-right' or 'bottom-left'
primaryColorstring'#4A56E2'Primary brand color (hex)
backgroundColorstring'#FFFFFF'Widget background color
textColorstring'#1F2937'Widget text color
launcherStylestring'bubble''bubble', 'bubble-text', 'icon-only', 'pill', 'square'
launcherGradientstring'solid''solid', 'sunset', 'ocean', 'forest', 'candy', 'midnight'
launcherShadowstring'medium''none', 'subtle', 'medium', 'strong', 'glow'
launcherAnimationstring'pulse''none', 'pulse', 'bounce', 'shake', 'glow'
launcherTextTypestring'static''static' (shows 'Rewards') or 'dynamic' (shows points)
animationDelaynumber5Seconds before first animation
animationRepeatnumber30Seconds between repeats (0 = no repeat)
animateOnScrollbooleantrueAnimate when user scrolls 25% down
bannerImagestring''URL to hero banner image
bannerLinkstring''URL the banner links to
bannerLinkNewTabbooleanfalseOpen banner link in new tab
showRewardsbooleantrueShow rewards tab
showReferralbooleantrueShow referral tab
showGuestMessagebooleantrueShow sign-in prompt for guests
accountUrlstring'/account'"View Account" link destination
loginUrlstring'/account/login'"Sign In" link for guests
registerUrlstring'/account/register'"Create Account" link for guests
translationsobject{}Override any default translation key

#Locale Support

32 built-in locales — set locale to auto-select:

descriptionLocale exampleJS
AnchorLoyaltySDK.init({ apiKey: '...', locale: 'de' }); // Deutsch
32 Supported locales
arالعربية
csČeština
daDansk
deDeutsch
elΕλληνικά
enEnglish
esEspañol
fiSuomi
frFrançais
fr-CAFrançais (CA)
heעברית
hiहिन्दी
huMagyar
idBahasa Indonesia
itItaliano
ja日本語
ko한국어
msBahasa Melayu
nlNederlands
noNorsk
plPolski
pt-BRPortuguês (BR)
pt-PTPortuguês (PT)
roRomână
ruРусский
svSvenska
thไทย
trTürkçe
ukУкраїнська
viTiếng Việt
zh-CN简体中文
zh-TW繁體中文
format_textdirection_r_to_l

RTL support: Arabic (ar) and Hebrew (he) locales automatically switch the widget layout to right-to-left, including text direction, icon positions, and animations.

Override individual keys:

descriptionOverride specific keysJS
AnchorLoyaltySDK.init({
  apiKey: '...',
  locale: 'fr',
  translations: { title: 'Mon Programme Fidélité' },
});

#Methods

AnchorLoyaltySDK.init(options)

Initialize the widget. Call once after the script loads.

AnchorLoyaltySDK.destroy()

Remove the widget completely. Cleans up:

  • - Widget DOM element
  • - Injected CSS and JS
  • - All window globals (anchorLoyaltyConfig, anchorLoyaltyTranslations, anchorLoyalty)

Safe for SPA route transitions.

AnchorLoyaltySDK.update(newOptions)

Update options and re-initialize (e.g., after login/logout). Preserves custom translations.

descriptionExample: Update after loginJS
// After user logs in:
AnchorLoyaltySDK.update({
  customerId: '12345',
  customerName: 'Jane',
});

#Architecture

Your Headless Storefront                    Anchor Loyalty API
(Hydrogen, Next.js, etc.)                   (anchorloyalty.app)
        |                                            |
        |  1. Load SDK                               |
        |  GET /api/sdk.js?key=ak_xxx                |
        |------------------------------------------->|
        |  <-- JS (widget + CSS + 32 locales)        |
        |                                            |
        |  2. Fetch customer data (automatic)        |
        |  GET /api/customer                         |
        |------------------------------------------->|
        |  <-- JSON (points, rewards, tier, etc.)    |
        |                                            |
        |  3. Redeem reward                          |
        |  POST /api/redeem                          |
        |------------------------------------------->|
        |  <-- JSON (discount code)                  |
        |                                            |
        |  4. Redeem reward (with customer token)    |
        |  POST /api/redeem                          |
        |  Headers: X-Anchor-Customer-Token:         |
        |    {customerId}.{ts}.{hmac}                |
        |------------------------------------------->|
        |  <-- JSON (discount code)                  |
vpn_key
Authentication
API key sent automatically by SDK
lock
Signing Secret
Used server-side only for customer token HMAC (never sent to browser)
public
CORS
Enabled for all origins
cached
Caching
SDK file cached 1hr (CDN)
speed
Rate limits
300 req/min per IP, 600 req/min per shop
lock
Customer Token
HMAC-signed proof for all authenticated requests

#Troubleshooting

IssueSolution
SDK returns 403Verify API key is correct and not revoked. Check PRO plan is active.
Widget doesn't appearCheck browser console for errors
"Customer not logged in"Verify customerId is the numeric Shopify ID, not the GID
Wrong languageSet locale parameter (e.g., 'de', 'fr', 'ja')
Widget overlaps UIAdjust position or use custom CSS
CORS errorsVerify you're loading SDK from anchorloyalty.app, not from a local file
Redemption returns 403 "Invalid or expired customer token"Verify your backend generates the token correctly. Check timestamp is within 5 minutes. Ensure customerId in token matches the request.
"API key required" on redemptionInclude the API key via X-Anchor-Api-Key header or api_key query param alongside the customer token.
"Signing secret not configured"Regenerate your API key from the dashboard. Both API key and signing secret will be shown once — store the signing secret in your backend environment variables.

Ready to add loyalty to your headless store?

Install Anchor, upgrade to Pro, and drop the SDK into your storefront. Full widget experience in minutes.