Your pricing page visitor arrived after clicking your Product Hunt post. She has one question about how billing works. Your blog reader is three posts into your documentation. He is not buying anything today.

Both land on your site and see the same chat widget with the same greeting and the same 30-second proactive timer. One missed conversion, one annoyed reader.

OperatorStack's AI chat widget is configured globally in your dashboard, but the JavaScript SDK gives you four tools to segment the experience per visitor: data-chat="false" to suppress chat on specific pages, OperatorStack.hideChat() for runtime conditions, OperatorStack.isSignedUp() to detect returning waitlist members, and OperatorStack.trackEvent() to log segment data for later analysis.

The Four Segmentation Tools

Most AI chat tools treat your entire site as one audience. OperatorStack does too, by default. The segmentation comes from combining the global dashboard config with SDK calls that run after initialization.

Your four tools, in order from simplest to most dynamic:

  1. data-chat="false" on the script tag -- suppress chat on specific pages with no JavaScript
  2. OperatorStack.hideChat() -- hide chat based on runtime conditions after load
  3. OperatorStack.isSignedUp() -- detect visitors who already joined your waitlist
  4. OperatorStack.trackEvent() -- log which segment converted, for dashboard analysis

Suppress Chat on Specific Pages

Some pages do not benefit from a chat widget. Blog posts, documentation, and "about" pages are reading destinations. A floating chat bubble interrupts the reading experience and rarely converts on content pages.

Add data-chat="false" to the script tag on any page where you want chat suppressed:

<!-- Blog posts, docs, about page: no chat bubble -->
<script
  src="https://cdn.operatorstack.dev/os.js"
  data-project="pk_abc123"
  data-chat="false"
></script>

<!-- Pricing page, homepage: chat allowed (omit the attribute) -->
<script src="https://cdn.operatorstack.dev/os.js" data-project="pk_abc123"></script>

Analytics, waitlist, and forms continue working on pages where chat is off. The attribute suppresses only the widget.

If your script tag comes from a shared layout template (common in Next.js, Astro, or Nuxt), you cannot easily add different attributes per page. Use the JavaScript approach below instead.

Hide Chat Based on Runtime Conditions

For dynamic conditions -- current pathname, query parameters, or visitor state -- use the SDK after initialization. All SDK methods are safe to call at any time; they internally await OperatorStack.ready.

// Hide chat on content pages and show it only on conversion pages
await OperatorStack.ready;

const conversionPaths = ["/", "/pricing", "/launch"];
const onConversionPage = conversionPaths.some(
  (p) => window.location.pathname === p || window.location.pathname.startsWith(p + "/"),
);

if (!onConversionPage) {
  OperatorStack.hideChat();
}

This pattern works on single-page apps where route changes happen in JavaScript. Call it again on each navigation if your SPA does not reload the page.

Detect Returning Waitlist Members

A visitor who already signed up does not need lead-capture chat. They are a qualified contact. Showing them "Want to know when we launch?" is worse than showing them nothing.

// Hide chat for visitors who already joined your waitlist
await OperatorStack.ready;

if (OperatorStack.isSignedUp()) {
  OperatorStack.hideChat();

  // Optionally surface a different CTA for returning signees
  document.getElementById("returning-member-cta")?.removeAttribute("hidden");
}

isSignedUp() checks localStorage for a signup identity written by joinWaitlist(). It returns true only when the visitor signed up on the same device and browser. Cross-device state is not tracked.

Do not use isSignedUp() as a security gate. It reads from localStorage, which the visitor can modify. Use it for UX personalization only, not access control.

Segment by Traffic Source

Product Hunt traffic, newsletter subscribers, and organic search visitors behave differently on the same landing page. You can detect the source from URL parameters or referrer, then tailor whether chat appears at all.

await OperatorStack.ready;

const params = new URLSearchParams(window.location.search);
const utmSource = params.get("utm_source") || "";
const fromNewsletter = utmSource === "newsletter" || utmSource === "email";

// Log which segment this visitor belongs to (shows up in your dashboard)
OperatorStack.trackEvent("segment_identified", {
  source: utmSource || "organic",
  from_newsletter: fromNewsletter,
  path: window.location.pathname,
});

// Newsletter subscribers already know you -- chat may feel intrusive
if (fromNewsletter) {
  OperatorStack.hideChat();
}

The trackEvent() call logs the segment in OperatorStack analytics. After a week, you can see which segments produce the most chat conversations and which convert to waitlist signups.

Configure Proactive Timing for High-Intent Pages

The proactive trigger timer is a global setting in your Chat dashboard: set it once, it fires on every page where chat renders. Combine it with page-based suppression so the timer only runs on your high-intent pages.

For a typical pre-launch SaaS with a homepage and a pricing page:

  • Set proactive_trigger_seconds to 20 in your Chat settings
  • Add data-chat="false" to blog and docs pages
  • Let chat auto-render on homepage and pricing -- the proactive trigger fires there and nowhere else

The result: visitors who reach your pricing page see a proactive message after 20 seconds, while blog readers never see the widget.

A Complete Example

A pre-launch SaaS with a homepage, pricing page, blog, and docs section:

// Runs on every page after the OperatorStack script tag loads
await OperatorStack.ready;

const path = window.location.pathname;
const alreadySigned = OperatorStack.isSignedUp();
const utmSource = new URLSearchParams(window.location.search).get("utm_source") || "direct";

// Determine if this page and visitor should see chat
const suppressChat = alreadySigned || path.startsWith("/blog") || path.startsWith("/docs");

if (suppressChat) {
  OperatorStack.hideChat();
}

// Track session context regardless
OperatorStack.trackEvent("session_start", {
  page_type:
    path === "/"
      ? "homepage"
      : path.startsWith("/pricing")
        ? "pricing"
        : path.startsWith("/blog")
          ? "blog"
          : "other",
  visitor_status: alreadySigned ? "signed_up" : "new",
  traffic_source: utmSource,
  chat_shown: !suppressChat,
});

The entire block runs synchronously after ready resolves. No server calls, no flicker on fast connections.

The AI chat knowledge base comes from your site scan, not from visitor attributes. It answers questions based on what is written on your landing page -- it cannot greet visitors by name or tailor its knowledge per segment. Segmentation controls when and where chat appears, not what the AI knows.

Frequently Asked Questions

Can I show a different welcome message to different visitor segments?

Not from a single dashboard config. The welcome message is a global setting in your Chat tab. To show segment-specific messages, combine SDK show/hide logic with separate OperatorStack projects configured for different audiences -- each project can have its own welcome message and quick actions.

How do I hide chat for visitors who already signed up?

Call OperatorStack.isSignedUp() after awaiting OperatorStack.ready. If it returns true, call OperatorStack.hideChat(). Signed-up users are past the lead-capture stage and rarely benefit from an AI chat prompt. Consider replacing the chat bubble with a feedback form or changelog link instead.

Does data-chat='false' affect analytics or waitlist tracking?

No. The attribute suppresses only the chat bubble. Analytics page views, waitlist signups, forms, and referral tracking all continue on pages where chat is off.

Can I show chat only on the pricing page?

Yes, two ways. Static: add data-chat="false" to the script tag on every page except pricing. Dynamic: await OperatorStack.ready, then call OperatorStack.hideChat() if window.location.pathname does not start with /pricing.

Will hideChat() flash the widget briefly before hiding it?

On fast connections, no. The widget loads asynchronously and hideChat() removes it before it becomes visible in most cases. For zero-flash suppression on pages where chat should never appear, use data-chat="false" on the script tag.