Storefront JavaScript events
Use cart, form, AI sign designer, lightbox, and ShareLink events from inline storefront JavaScript.
Storefront JavaScript events let theme code respond to the customer customiser on the same page. They cover cart actions, forms, the AI sign designer, lightbox sizing, and ShareLinks.
These browser integrations use CustomEvent on document. They are not REST endpoints.
Browser scope
Listen on the storefront document. With an inline customer customiser, all events in this guide reach this document.
With an iframe embed, the Sign Customiser iframe wrappers (the Shopify embed script, the WooCommerce plugin, the Wix embed, and the generic embed element) dispatch these events on the storefront document:
- The cart events, when the platform sends them. See Add-to-cart tracking and Checkout tracking.
signCustomiserFormSubmittedandsignCustomiserQuoteSubmitted.- The ShareLink events. The wrapper relays
signCustomiserShareLinkRequestedto the customer customiser iframe. See Request a ShareLink.
The AI sign designer events and signCustomiserLightboxSizeMatched stay inside the iframe. They do not reach the storefront document.
Add listeners before the action that sends an event. Remove temporary listeners when your integration finishes.
Event list
| Event | Direction | When it occurs |
|---|---|---|
triggerUpdateCart | Customer customiser to theme | A product enters the Shopify cart and the Store uses a custom cart action. |
signCustomiserProductAddedToCart | Customer customiser to theme | A product is added to the Shopify cart, or a generic embed with the custom driver hands off the product. WooCommerce and Wix do not send it. |
signCustomiserCheckoutStarted | Customer customiser to theme | A shopper is about to go to checkout, with the Buy it now button or the Straight to Checkout cart action. |
signCustomiserFormSubmitted | Customer customiser to theme | A custom design form submission succeeds. |
signCustomiserQuoteSubmitted | Customer customiser to theme | A quote form submission succeeds. |
signCustomiserAiLogoCompleted | Customer customiser to theme | The AI sign designer completes a design. |
signCustomiserAiLogoSuccess | Customer customiser to theme | The AI sign designer completes a design. This event fires with the completed event. |
signCustomiserAiLogoFailed | Customer customiser to theme | The AI sign designer cannot complete a design and uses its fallback. |
signCustomiserAiLogoInsufficientCredits | Customer customiser to theme | The Store has no AI sign designer credits. |
signCustomiserLightboxSizeMatched | Customer customiser to theme | A lightbox face image reaches automatic size matching. |
signCustomiserShareLinkRequested | Theme to customer customiser | Theme code requests a ShareLink for the current design. |
signCustomiserShareLinkCreated | Customer customiser to theme | ShareLink creation succeeds. |
signCustomiserShareLinkFailed | Customer customiser to theme | ShareLink creation fails. |
Listen for an event
Add a listener to document:
function handleProductAdded(event) { useCartResult(event.detail.cart);}
document.addEventListener( "signCustomiserProductAddedToCart", handleProductAdded,);Replace useCartResult with your integration code.
Cart events
Custom cart hand-off
triggerUpdateCart fires after an inline customer customiser adds a product to Shopify. It fires only when the Store uses the custom cart action.
The event detail is the Shopify cart response:
type TriggerUpdateCartDetail = Record<string, unknown>;Use this event to update a cart drawer or start another custom cart step.
Add-to-cart tracking
signCustomiserProductAddedToCart fires after a successful Shopify cart request. This includes the inline embed, the Shopify iframe embed, and the Sign Customiser Modal block. It also fires for a generic embed with the custom driver, when the customer customiser hands off the product. The generic embed does not confirm a cart add, and cart can be undefined. It fires before a cart redirect or custom cart hand-off. WooCommerce and Wix embeds do not send this event.
type SignCustomiserProductAddedToCartDetail = { customiserId: string | number | null | undefined; domain: string | number | null | undefined; cart: Record<string, unknown> | null | undefined; intent: "cart" | "checkout"; adClickIds: Record<string, string> | null; utmParams: Record<string, string> | null;};intent is "checkout" when the shopper used the Buy it now button. For all other adds it is "cart".
For the inline Shopify embed, this event is not available to Starter, Standard, or Pro plan families. This restriction includes yearly variants. Embeds that use an iframe send the event for every plan.
Checkout tracking
signCustomiserCheckoutStarted fires immediately before the page goes to checkout. It fires for the Buy it now button and, on Shopify, for the Straight to Checkout cart action. On Shopify it fires after signCustomiserProductAddedToCart. On WooCommerce there is no add-to-cart event, so this event is the only one. It does not fire when the shopper stays on the page or goes to the cart page.
On WooCommerce, the Buy it now button goes to the checkout page of the store. The customiser finds this page from the checkout-url attribute of the embed element, then checkout_url from the Sign Customiser WordPress plugin (version 1.6.3 and later), then the WooCommerce checkout settings of the page. If none of these exists, the customiser checks the checkout page next to the WooCommerce cart page. If the page cannot be confirmed, the shopper gets the normal cart behaviour of the store and this event does not fire.
The event detail has the same shape as signCustomiserProductAddedToCart. The cart value is the cart response of the platform.
type SignCustomiserCheckoutStartedDetail = SignCustomiserProductAddedToCartDetail;The Buy it now button does not fire triggerUpdateCart. For the inline Shopify embed, this event is not available to Starter, Standard, or Pro plan families. Embeds that use an iframe send the event for every plan.
Form submission events
signCustomiserFormSubmitted fires after a custom design form submission succeeds. signCustomiserQuoteSubmitted fires after a quote form submission succeeds.
Both events use this detail:
type SignCustomiserFormSubmittedDetail = { quoteId: number | null; customiserId: string | number | null | undefined; formId: string | number; domain: string | null; adClickIds: Record<string, string> | null; utmParams: Record<string, string> | null; data: Array<{ fieldId: string; label: string | undefined; value: string | File | Array<string | File>; urls?: string[]; }>;};quoteId is the ID of the saved submission. It is the same value as id in the quotes API. Use it to read the submission with Retrieve a quote. The value is null when the response has no saved submission ID.
Use formId and fieldId to map the submission to your system. Do not use label text as a stable field identifier.
These events are not available to Starter, Standard, or Pro plan families. This restriction includes yearly variants.
AI sign designer events
signCustomiserAiLogoCompleted and signCustomiserAiLogoSuccess fire together after the AI sign designer completes a design.
Both events use this detail:
type SignCustomiserAiLogoCompletedDetail = { svg: string | null; svgUrl: string | null; rawUrl: string | null;};signCustomiserAiLogoFailed fires when the AI sign designer uses its failure fallback. signCustomiserAiLogoInsufficientCredits fires when the Store has no credits. These events have no detail fields.
The URLs and SVG can contain customer design data. Send this data only to systems that need the design.
Lightbox size event
signCustomiserLightboxSizeMatched fires when a lightbox face image reaches automatic size matching.
type SignCustomiserLightboxSizeMatchedDetail = { outcome: | "matched" | "skipped-not-offered" | "skipped-no-image" | "skipped-empty-envelope";};matched means that the customer customiser applied an image-matched custom size. A skipped-* value identifies why the customer customiser kept the current size.
Request a ShareLink
Use signCustomiserShareLinkRequested when theme code needs a ShareLink for the current design.
Do not click the customer Share button from theme JavaScript. Do not read, close, or remove the Share modal. The customer customiser owns this interface.
The request does not open a modal, move focus, or change the current screen.
ShareLink event names
| Direction | Event name |
|---|---|
| Theme to customer customiser | signCustomiserShareLinkRequested |
| Customer customiser to theme, success | signCustomiserShareLinkCreated |
| Customer customiser to theme, failure | signCustomiserShareLinkFailed |
While the runtime remains mounted, each valid request receives one success or failure event.
Inline and iframe embeds
Dispatch the request on the storefront document in both embed modes:
-
An inline customer customiser answers the request directly.
-
A Sign Customiser iframe wrapper forwards the request to its customer customiser iframe and dispatches the reply on the storefront
document. A wrapper with no mounted iframe, for example a closed modal, answers withsignCustomiserShareLinkFailedand the codenot_ready. The wrapper also answersnot_readywhen the modal closes during a request, or when the customer customiser does not reply within 15 seconds.
Use one customer customiser per page for this integration. Each mounted customer customiser answers every request.
An iframe that you add to a page yourself, without a Sign Customiser wrapper, does not receive the request.
Request detail
Dispatch signCustomiserShareLinkRequested with this detail:
type SignCustomiserShareLinkRequestedDetail = { requestId: string;};requestId must match this pattern:
[A-Za-z0-9._:-]{1,128}Create a new value for each pending request. Use it only to match the result to the request. It is not a server idempotency key.
The request does not accept callbacks, DOM nodes, cache data, customiser IDs, host URLs, or endpoint URLs. The mounted customer customiser supplies these values.
The customer customiser ignores invalid detail. Invalid detail does not create a ShareLink or change the design.
Success detail
Listen for signCustomiserShareLinkCreated:
type SignCustomiserShareLinkCreatedDetail = { requestId: string; customiserId: string; domain: string | null; url: string;};The customer customiser returns requestId without changes. Use it to match a result to a pending request.
url is the complete ShareLink. Its security value is in the URL fragment. Store the complete returned URL. Do not build a URL from token parts.
Failure detail
Listen for signCustomiserShareLinkFailed:
type SignCustomiserShareLinkFailedDetail = { requestId: string; customiserId: string | null; domain: string | null; code: "not_ready" | "sharing_unavailable" | "request_failed";};Use code to select the recovery action:
| Code | Meaning |
|---|---|
not_ready | The customer customiser is not ready to create a ShareLink. |
sharing_unavailable | The Store is not eligible to create ShareLinks. |
request_failed | A request or server problem stopped ShareLink creation. |
not_ready can occur before the runtime knows the customiser or domain. Therefore, customiserId and domain can be null.
Failure events do not contain response bodies, stack traces, cache data, URLs, or token fields.
Eligibility
The Store must be able to go live. Its plan must include sharing.
Settings such as shareLocation control the visible customer Share control. They do not block an eligible request after the runtime is ready.
Privacy and expiry
A ShareLink is a bearer link to a saved design. Anyone with the complete URL can open the design until it expires or you revoke it.
Each ShareLink expires 365 days after creation. Store the complete URL only in the workflow that needs the design.
To stop access before expiry, open Shares in the merchant admin, open the saved share, and select Revoke ShareLink. Revocation cannot be undone.
Do not log the ShareLink URL. Do not send it to analytics. Do not extract, build, or log token parts.
Complete ShareLink example
This example requests a ShareLink before a cart flow. It adds both terminal listeners before the request. It removes them after success, failure, or timeout.
function requestSignCustomiserShareLink(options = {}) { const timeoutMs = options.timeoutMs ?? 10000; const requestId = options.requestId ?? `cart-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
return new Promise((resolve, reject) => { let settled = false;
function cleanup() { document.removeEventListener( "signCustomiserShareLinkCreated", handleCreated, ); document.removeEventListener( "signCustomiserShareLinkFailed", handleFailed, ); window.clearTimeout(timeout); }
function settle(action, value) { if (settled) { return; }
settled = true; cleanup(); action(value); }
function handleCreated(event) { if (event.detail?.requestId !== requestId) { return; }
settle(resolve, event.detail.url); }
function handleFailed(event) { if (event.detail?.requestId !== requestId) { return; }
settle( reject, new Error(`ShareLink request failed: ${event.detail.code}`), ); }
const timeout = window.setTimeout(() => { settle(reject, new Error("ShareLink request timed out.")); }, timeoutMs);
document.addEventListener( "signCustomiserShareLinkCreated", handleCreated, ); document.addEventListener( "signCustomiserShareLinkFailed", handleFailed, );
document.dispatchEvent( new CustomEvent("signCustomiserShareLinkRequested", { detail: { requestId }, }), ); });}
async function continueCartFlow() { try { const shareLinkUrl = await requestSignCustomiserShareLink(); await saveShareLinkForCart(shareLinkUrl); } catch { // Continue without a ShareLink. }
await continueToCart();}Replace saveShareLinkForCart and continueToCart with your existing cart code.
Private implementation
Do not call private ShareLink routes from theme code. Storefront JavaScript events are the supported merchant integration.