Sign Customiser

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 same document as an inline customer customiser. These events do not support messaging across a cross-origin iframe.

Add listeners before the action that sends an event. Remove temporary listeners when your integration finishes.

Event list

EventDirectionWhen it occurs
triggerUpdateCartCustomer customiser to themeA product enters the Shopify cart and the Store uses a custom cart action.
signCustomiserProductAddedToCartCustomer customiser to themeA supported tracking plan adds a product to the Shopify cart.
signCustomiserFormSubmittedCustomer customiser to themeA custom design form submission succeeds.
signCustomiserQuoteSubmittedCustomer customiser to themeA quote form submission succeeds.
signCustomiserAiLogoCompletedCustomer customiser to themeThe AI sign designer completes a design.
signCustomiserAiLogoSuccessCustomer customiser to themeThe AI sign designer completes a design. This event fires with the completed event.
signCustomiserAiLogoFailedCustomer customiser to themeThe AI sign designer cannot complete a design and uses its fallback.
signCustomiserAiLogoInsufficientCreditsCustomer customiser to themeThe Store has no AI sign designer credits.
signCustomiserLightboxSizeMatchedCustomer customiser to themeA lightbox face image reaches automatic size matching.
signCustomiserShareLinkRequestedTheme to customer customiserTheme code requests a ShareLink for the current design.
signCustomiserShareLinkCreatedCustomer customiser to themeShareLink creation succeeds.
signCustomiserShareLinkFailedCustomer customiser to themeShareLink creation fails.

Listen for an event

Add a listener to document:

js
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:

ts
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 inline Shopify cart request. It fires before a cart redirect or custom cart hand-off.

ts
type SignCustomiserProductAddedToCartDetail = {
customiserId: string | number | null;
domain: string | null;
cart: Record<string, unknown>;
adClickIds: Record<string, string> | null;
utmParams: Record<string, string> | null;
};

This event is not available to Starter, Standard, or Pro plan families. This restriction includes yearly variants.

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:

ts
type SignCustomiserFormSubmittedDetail = {
customiserId: string | number | null;
formId: string | number;
domain: string | null;
adClickIds: Record<string, string> | null;
utmParams: Record<string, string> | null;
data: Array<{
fieldId: string;
label: string;
value: string | File | Array<string | File>;
urls?: string[];
}>;
};

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:

ts
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.

signCustomiserLightboxSizeMatched fires when a lightbox face image reaches automatic size matching.

ts
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.

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.

DirectionEvent name
Theme to customer customisersignCustomiserShareLinkRequested
Customer customiser to theme, successsignCustomiserShareLinkCreated
Customer customiser to theme, failuresignCustomiserShareLinkFailed

While the runtime remains mounted, each valid request receives one success or failure event.

Request detail

Dispatch signCustomiserShareLinkRequested with this detail:

ts
type SignCustomiserShareLinkRequestedDetail = {
requestId: string;
};

requestId must match this pattern:

text
[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:

ts
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:

ts
type SignCustomiserShareLinkFailedDetail = {
requestId: string;
customiserId: string | null;
domain: string | null;
code: "not_ready" | "sharing_unavailable" | "request_failed";
};

Use code to select the recovery action:

CodeMeaning
not_readyThe customer customiser is not ready to create a ShareLink.
sharing_unavailableThe Store is not eligible to create ShareLinks.
request_failedA 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.

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.

js
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.