Deploy and verify

Ship your Joy headless integration to Oxygen safely — environment variables, full-page caching, a preview deploy and a guest and member checklist.

Overview

Most "works locally, broken in production" issues come from two things: environment variables missing in one Oxygen environment, and cached pages showing one customer's view to another. This page covers both, then gives you a checklist to run before going live.

Step 1: Add environment variables in Oxygen

In Shopify admin > Hydrogen > your storefront > Storefront settings > Environment variables, add for both Preview and Production (they are separate):

VariableValue
JOY_API_URLhttps://api.joy.so/app/api/v1/headless
JOY_SHOP_DOMAINyour-store.myshopify.com
JOY_API_SECRETJoy admin > Settings > Manage keys > Headless secret (click Generate headless secret) — mark it as secret
PUBLIC_CHECKOUT_DOMAINYour checkout domain (your myshopify domain is fine)

On @joy-loyalty/hydrogen 0.2.1 or later, don't set JOY_WIDGET_ASSET_URL. On 0.2.0, add JOY_WIDGET_ASSET_URL=https://api.joy.so too — the 0.2.0 default doesn't serve the widget. Never point JOY_API_URL at a local or tunnel address.

Step 2: Check your full-page cache rules

Oxygen can cache whole pages to make your store faster. The catch: Oxygen's cache ignores cookies, so a page cached for a guest can be shown to a logged-in customer — and the widget then says "Sign in" to someone who is already signed in.

Only add Oxygen-Cache-Control to a page when all of these are true:

  • It is a GET request with a 200 response.
  • The page has no personalized Joy data (isJoyInitialDataPersonalized returns false).
  • The request has no customer session cookie.
  • The path (after any locale prefix) doesn't start with account, api, cart, discount, graphiql or subrequest-profiler.
  • It isn't a Weaverse Studio preview (no weaverseHost, isDesignMode, __revisionId and similar parameters).

When you do cache, also set a Vary header. If your app handles React Router data requests separately (for example in handleDataRequest), apply the same rules there.

Pages for logged-in customers must use Cache-Control: private, no-store — the code in Install manually already does this.

Step 3: Deploy to Preview first

  1. Commit your code — without .env.
  2. Deploy with shopify hydrogen deploy or your GitHub integration to a Preview environment.
  3. Run the checklist below on the preview URL.
  4. Promote to Production when every box is ticked.

There is nothing to deploy on the Joy side — changes you make in Joy admin reach your storefront automatically.

Step 4: Verify

As a guest (incognito window):

  • The loyalty launcher appears on every page.
  • The browser console shows no Content Security Policy errors mentioning joy.so.
  • /api/joy-token returns 401 not_logged_in once — and doesn't repeat.
  • The Sign in button goes to your login page.
  • Your Loyalty Page shows the guest sections.
  • Cacheable pages have Oxygen-Cache-Control; account, cart and API pages don't.

As a logged-in customer:

  • Page responses have Cache-Control: private, no-store and no Oxygen-Cache-Control.
  • /api/joy-token returns 200 with a token.
  • Points and tier match the customer in Joy admin.
  • Redeeming a reward applies the coupon to the cart and opens the cart drawer.
  • Switching storefront language (for example /fr/...) switches Joy texts.
  • Logging out returns the widget to the guest view.

Something not right? See Troubleshooting.