Customizer – Basic embedding on your website

Full Screen Embedding

Embed the Customizer 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 iframe.
  2. The iframe and your website perform a postMessage handshake (nonce).
  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 responds with ALTER_TOOL_SESSION_READY.

Tip: session tokens are typically bound to the host origin. Generate tokens server-side for the exact domain that embeds the iframe.

<!DOCTYPE html>
<html>
  <head>
    <title>Customizer - Full Screen (Token + postMessage)</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; }
      #wrapper { width: 100%; height: 100%; }
      iframe { width: 100%; height: 100%; border: 0; display: block; }
    </style>
  </head>
  <body>
    <div id="wrapper">
      <iframe
        id="alter-iframe"
        title="Alter Product Customizer"
        src="https://alterproduct.com/app/customizer/11"
        allowfullscreen
      ></iframe>
    </div>

    <script>
      const IFRAME_ORIGIN = "https://alterproduct.com";
      const iframe = document.getElementById("alter-iframe");

      let handshakeOk = false;

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

      /**
       * Your backend MUST request the session token (recommended).
       * This endpoint is owned by YOU and should call Alter Product API using server-side credentials.
       * Expected response: { sessionToken: "..." }
       */
      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();

        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 || {};
        if (!msg.type || typeof msg.type !== "string") return;

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

          handshakeOk = true;
          postToIframe({ type: "ALTER_PARENT_ACK", nonce });
          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 || "customizer").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 Customizer events
        // -----------------------
        if (msg.type === "ALTER_TOOL_READY") {
          console.log("[Alter] Customizer ready:", msg);
          return;
        }

        if (msg.type === "ALTER_TOOL_ERROR") {
          console.error("[Alter] Customizer error:", msg);
          return;
        }

        // Add your Customizer-specific events here (if you expose them)
        // e.g. ALTER_CUSTOMIZER_SAVE, ALTER_CUSTOMIZER_EXPORT, etc.
      });
    </script>
  </body>
</html>

Basic Styling

You can customize the Customizer in two ways:

  • Using URL parameters – configure background, 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;
}