Guest → customer merge
How an anonymous wishlist becomes the customer's when a shopper logs in — and what your integration has to do (usually nothing).
This page explains what happens to a guest wishlist at login, and the one call headless integrations make to trigger it.
The model
A shopper who isn't logged in saves into an anonymous list, keyed by an opaque anonymousId
generated in their browser (stored in localStorage as envie:aid). Knowing the id is the
access: it's a capability, like an unguessable URL — not an identity, not a session.
When that shopper logs in, the anonymous list merges into their customer list:
- Items are copied over; a product already on the customer list is not duplicated.
- The anonymous list is deleted. The
anonymousIdnow points at nothing. - Original
addedAttimestamps are preserved. - A
list.mergedevent fires (webhooks, integrations), with the count of items actually moved.
The merge is idempotent and replay-safe: calling it twice, or from two tabs racing each other, merges once — the loser finds nothing left to merge and gets the customer's list back unchanged.
Liquid storefronts: nothing to do
The app embed tells the client who's logged in on every page load. First page view after login → merge happens automatically. Both the guest saves and the customer's earlier saves are in the list; the badge count updates.
Headless storefronts: one server-side call
A browser holding a public key can never assert "I am customer 42" — identity must come from your server, after your own authentication:
import { createEnvie } from "@getenvie/client";
// In your login handler, after the customer is authenticated:
const envie = createEnvie({
mode: "private",
shop: "your-store.myshopify.com",
secretKey: process.env.ENVIE_SECRET_KEY!, // env_sk_… — server only
anonymousId, // the shopper's `envie:aid`, sent up from the browser
});
const mergedList = await envie.identify({ customerId });The response is the merged list — hand it back to the browser if you want the UI to update without a refetch.
Getting the anonymousId to your server: read localStorage.getItem("envie:aid") in the browser
and include it in your login request (a form field, a JSON property — whatever your auth flow
uses).
Watching merges happen
- Server: subscribe to
list.mergedvia outbound webhooks —data: { listId, customerId, mergedCount }. - Klaviyo: the merge forwards as
Envie Wishlist List Mergedwhen the integration is on — useful for a "welcome back, your wishlist is safe" flow.
Common mistakes
- Calling
identify()from the browser in public mode. Rejected, by design (identify-forbidden). Route it through your server. - Reusing one server-side client for every shopper.
anonymousIdbinds at construction. Build the client per request, with that shopper's id. - Expecting the anonymous list to survive. After the merge it's gone. If your UI caches list
state keyed by
anonymousId, refetch after login. - Treating
anonymousIdas PII-free session data to log freely. It grants read/write access to that wishlist. Don't put it in URLs you share or logs you keep long-term.