# Storefront JavaScript events

Request a current design link from inline storefront JavaScript without driving customer controls.

- Source URL: https://www.signcustomiser.com/help/api/guides/storefront-javascript-events/
- Markdown URL: https://www.signcustomiser.com/help/api/guides/storefront-javascript-events.md

## Guide

Storefront JavaScript events let theme code talk to an inline customer customiser on the same page. Use them when your theme needs the current design link before it continues a Shopify cart or checkout flow.

This guide covers the design-link event contract. It is a browser integration, not REST. The private shopper share endpoint stays private. It is not part of the public `/api/v3` OpenAPI reference.

## When to use this event

Use `signCustomiserDesignLinkRequested` when theme code needs a shareable URL for the customer's current design.

Do not click the customer **Share** button from theme JavaScript. Do not read, close, or remove the Share modal. React owns that interface. Direct DOM changes can leave the customer control in the wrong state.

The event request runs headlessly. It does not open a modal, move focus, change the active screen, or change customer Share controls.

## Browser scope

The events work on `document` for an inline customer customiser embed. The request and terminal event must use the same browser document.

This contract does not define cross-origin iframe messaging. If your customiser runs inside a cross-origin iframe, the parent page needs a separately supported bridge before it can use this protocol.

## Event names

| Direction | Event name |
| --- | --- |
| Theme to customer customiser | `signCustomiserDesignLinkRequested` |
| Customer customiser to theme, success | `signCustomiserDesignLinkCreated` |
| Customer customiser to theme, failure | `signCustomiserDesignLinkFailed` |

While the customer customiser runtime remains mounted, each valid request receives exactly one terminal event, either success or failure.

## Request event

Dispatch `signCustomiserDesignLinkRequested` with this detail:

```ts
type SignCustomiserDesignLinkRequestedDetail = {
  requestId: string;
};
```

`requestId` must match this pattern:

```text
[A-Za-z0-9._:-]{1,128}
```

Generate a new value for each pending request. The value is for correlation only. It is not an idempotency key. Reusing it after a terminal event does not promise a replayed result.

The request does not accept callbacks, DOM nodes, serialised cache data, a customiser ID, a host URL, or an endpoint URL. The customer customiser reads those values from its mounted runtime.

Invalid detail is ignored safely. It does not make a network request or change customer-customiser state.

## Success event

Listen for `signCustomiserDesignLinkCreated`. The event detail has this shape:

```ts
type SignCustomiserDesignLinkCreatedDetail = {
  requestId: string;
  customiserId: string;
  domain: string | null;
  url: string;
};
```

`requestId` is returned unchanged so that you can match the result to your request. `customiserId`, `domain`, and `url` come from the mounted customer customiser. Theme code cannot override them.

`url` is the complete storefront URL with the share token applied. Save that complete URL. Do not build your own URL from token parts.

## Failure event

Listen for `signCustomiserDesignLinkFailed`. The event detail has this shape:

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

Use `code` to choose recovery behaviour:

| Code | Meaning |
| --- | --- |
| `not_ready` | The customer customiser runtime is not ready to create a link. |
| `sharing_unavailable` | The Store is not eligible to create share links. |
| `request_failed` | A network, server, response, token, or URL problem stopped link creation. |

`not_ready` can occur before the runtime knows the customiser or domain. Thus, `customiserId` and `domain` can be `null`.

Failure events do not expose response bodies, stack traces, raw HTTP status text, the serialised design cache, or a share token field.

## Eligibility

The design-link event follows the same server-side sharing entitlement as the customer Share feature. The Store must be able to go live, and its plan must include sharing.

Some settings only control where customer Share controls appear. For example, `shareLocation` controls Share control placement. Content checks can hide customer controls for some designs. These controls do not block a headless merchant request after the design runtime is ready.

## Privacy

The returned design URL is a bearer link to the saved design. Anyone with the URL can open the saved design while the link remains valid.

The event exposes the URL to scripts that run in the same document as the customer customiser. Do not log the URL, send it to analytics, or expose it outside the workflow that needs it.

The event does not expose the serialised design cache. It also does not expose the share token as a separate field.

## Complete example

This example asks for the current design URL. Then it saves the URL before a cart flow continues. It installs both terminal listeners before it dispatches the request. It removes both listeners after success, failure, or timeout.

```js
function requestSignCustomiserDesignLink(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(
        "signCustomiserDesignLinkCreated",
        handleCreated,
      );
      document.removeEventListener(
        "signCustomiserDesignLinkFailed",
        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(`Sign Customiser design link failed: ${event.detail.code}`),
      );
    }

    const timeout = window.setTimeout(() => {
      settle(
        reject,
        new Error("Sign Customiser design link request timed out."),
      );
    }, timeoutMs);

    document.addEventListener(
      "signCustomiserDesignLinkCreated",
      handleCreated,
    );
    document.addEventListener("signCustomiserDesignLinkFailed", handleFailed);

    document.dispatchEvent(
      new CustomEvent("signCustomiserDesignLinkRequested", {
        detail: { requestId },
      }),
    );
  });
}

async function continueCartFlow() {
  try {
    const designUrl = await requestSignCustomiserDesignLink();

    await saveDesignUrlForCart(designUrl);
    await continueToCart();
  } catch (error) {
    console.error(error);
    await continueToCart();
  }
}
```

Replace `saveDesignUrlForCart` and `continueToCart` with your theme's existing cart code.

## Request timing and duplicate work

The customer customiser captures the design snapshot when it receives a valid request. If two overlapping requests capture the same frozen design snapshot, the customer customiser shares the same in-flight link-generation work. Each request still receives its own terminal event with its own `requestId`.

If the design changes between overlapping requests, the second request uses a new snapshot. After an operation settles, a later request starts new link-generation work even if the design is unchanged. It does not replay the earlier result.

## Do not call the shopper endpoint

The shopper share endpoint that stores the design is private. Do not call it from theme code. Do not send it your own serialised cache.

This event is the public storefront contract for design-link capture. The shopper endpoint is not a public REST endpoint. It is not documented in the `/api/v3` OpenAPI reference.
