Configurator – Basic embedding on your website

Full Screen Embedding

Embed the Configurator anywhere on your website using an iframe.

Important: embedding requires a session token and a postMessage handshake. Public/private visibility does not affect embedding — the token controls access.

Flow:

  1. Your website loads the Configurator iframe.
  2. The iframe and your website perform a postMessage handshake.
  3. The iframe requests a session token from your website (ALTER_TOOL_INIT_SESSION).
  4. Your backend requests the token from Alter Product API and your website sends it back to the iframe (ALTER_TOOL_SESSION_READY).
<!DOCTYPE html>
<html>
  <head>
    <title>Alter Product - Full Screen Embed</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style>
      * { box-sizing: border-box; }
      html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
      iframe { width: 100%; height: 100%; border: 0; display: block; }
      #wrapper { width: 100%; height: 100%; }
    </style>
  </head>
  <body>
    <div id="wrapper">        
      <iframe
        id="alter-iframe"
        title="Alter Product Tool"
        src="https://alterproduct.com/app/configurator/1"
        allowfullscreen
      ></iframe>
    </div>
    <script>
      /**
       * ✅ CONFIG
       * Tool URL examples:
       * - Configurator:       https://alterproduct.com/app/configurator/1
       */
      const IFRAME_ORIGIN = "https://alterproduct.com";
      const iframe = document.getElementById("alter-iframe");

      // Keep nonce per handshake (from iframe) to prevent random messages from being accepted.
      let handshakeNonce = "";
      let handshakeOk = false;

      function postToIframe(payload) {
        iframe.contentWindow.postMessage(payload, IFRAME_ORIGIN);
      }

      /**
       * 1) Your backend must request the session token.
       * This function calls YOUR backend endpoint (recommended),
       * which then calls Alter Product API using your server-side credentials.
       */
      async function getSessionTokenFromYourBackend({ tool, mode, uiDesignId }) {
        const url = new URL("/api/alter/session-token", window.location.origin);
        url.searchParams.set("tool", tool);               // viewer | configurator | customizer
        url.searchParams.set("mode", mode || "design");   // e.g. "design"
        url.searchParams.set("uiDesignId", String(uiDesignId || 0));

        const res = await fetch(url.toString(), { method: "GET" });
        if (!res.ok) throw new Error("Failed to get session token");
        const data = await res.json();

        // Expect: { sessionToken: "..." }
        if (!data || !data.sessionToken) throw new Error("Missing sessionToken");
        return data.sessionToken;
      }

      window.addEventListener("message", async (event) => {
        // ✅ 1) Always validate origin
        if (event.origin !== IFRAME_ORIGIN) return;

        // ✅ 2) Always validate source
        if (event.source !== iframe.contentWindow) return;

        const msg = event.data || {};

        // -----------------------
        // A) HANDSHAKE
        // iframe -> parent: ALTER_CHILD_HELLO { nonce }
        // parent -> iframe: ALTER_PARENT_ACK  { nonce }
        // -----------------------
        if (msg.type === "ALTER_CHILD_HELLO") {
          if (!msg.nonce || typeof msg.nonce !== "string") return;

          handshakeNonce = msg.nonce;
          handshakeOk = true;

          postToIframe({ type: "ALTER_PARENT_ACK", nonce: handshakeNonce });
          return;
        }

        // Ignore everything until handshake is done
        if (!handshakeOk) return;

        // -----------------------
        // B) TOKEN REQUEST (on-demand)
        // iframe -> parent: ALTER_TOOL_INIT_SESSION
        //   payload: { tool, mode, uiDesignId }
        //
        // parent -> iframe: ALTER_TOOL_SESSION_READY
        //   { token }
        // -----------------------
        if (msg.type === "ALTER_TOOL_INIT_SESSION") {
          try {
            const payload = msg.payload || {};

            const tool = String(payload.tool || "viewer").toLowerCase();
            const mode = String(payload.mode || "design");
            const uiDesignId = Number(payload.uiDesignId || 0);

            const token = await getSessionTokenFromYourBackend({
              tool,
              mode,
              uiDesignId
            });

            postToIframe({
              type: "ALTER_TOOL_SESSION_READY",
              token
            });
          } catch (e) {
            postToIframe({
              type: "ALTER_TOOL_SESSION_ERROR",
              error: String(e && e.message ? e.message : e)
            });
          }
          return;
        }

        // -----------------------
        // C) OPTIONAL: listen to tool events (examples)
        // -----------------------
        if (msg.type === "ALTER_TOOL_READY") {
          console.log("[Alter] Tool ready:", msg);
          return;
        }

        if (msg.type === "ALTER_VIEWER_ADD_TO_CART") {
          console.log("[Alter] Add to cart:", msg.payload);
          return;
        }
      });
    </script>
  </body>
</html>

Basic Styling

You can customize the Configurator in two ways:

  • Using URL parameters – configure environment, UI options, etc. (see Styling (URL Parameters)).
  • Using CSS on the container element around the iframe (size, borders, radius, layout).
#wrapper {
  height: 320px;
  width: 400px;
  overflow: hidden;
  border-radius: 14px;
  background: linear-gradient(0deg, rgba(0,43,133,1) 0%, rgba(0,182,215,1) 100%);
}

iframe {
  width: 100%;
  height: 100%;
  display: block;
  border: 0;
}