Traffic Classification

ClickStream labels every accepted event with a traffic classification. The label tells you which lane the event belongs in:

Non-human traffic is not automatically bad. It is measured, labeled, and kept separate so you can use it without letting it distort human usage, support routing, or reporting.

The Two Labels You Will Use Most

visitor.bot.isBot        // true when this event is bot-classed
visitor.behavioralClass  // "human", "suspicious", "likely_bot", or "bot"

Use them together:

if (!visitor.bot.isBot && visitor.behavioralClass === 'human') {
  showHumanOnlyExperience();
} else {
  keepOutOfHumanMetrics();
}

How Classification Works

ClickStream combines several kinds of evidence. No single signal decides the final label by itself.

Signal familyWhat it helps catch
Edge request signalsKnown crawlers, preview renderers, monitoring checks, suspicious network context.
User-agent recognitionSearch crawlers, answer agents, SEO tools, previews, monitors, scanners, and automation tools.
Browser automation signalsControlled browsers, test harnesses, and scripted review sessions.
Human interaction confidenceClicks, scroll depth, form use, timing, hesitation, page sequence, and session diversity.
Consistency checksSessions that claim to be ordinary browsers but have mismatched device, timing, or interaction evidence.

The result is a bot.score from 0 to 100. Think of it as automation likelihood: higher means less person-like.

Behavioral Buckets

BucketMeaningTypical action
humanStrong person evidence and low automation likelihood.Eligible for human-only UI and human analytics.
suspiciousSome automation-like or low-confidence signals.Measure, but avoid human-only actions until more evidence appears.
likely_botStrong automation-like behavior without a definitive named match.Keep out of human-only actions; review if important.
botClassified as non-human.Route to the right non-human lane.

For support, personalization, and human-only reporting, prefer behavioralClass === 'human' plus bot.isBot === false.

Bot Categories

When ClickStream can name the non-human purpose, visitor.bot.category uses this taxonomy:

CategoryMeaning
search_crawlerSearch indexing crawler.
seo_toolSEO analysis or audit crawler.
ai_agentAnswer-engine, retrieval, or AI-reading agent.
social_previewLink preview or embed renderer.
monitoringUptime or synthetic monitoring check.
scraperProgrammatic downloader or generic scraper.
scannerSecurity scanner or probe-like traffic.
automationControlled browser, QA run, or scripted review session.
stealth_botBot-like behavior that avoided simple name-based recognition.
kioskInternal display, passive screen, or long-lived unattended browser lane.
unknown_botBot-shaped traffic with no specific category yet.

The category tells you the safest lane. For example, ai_agent and search_crawler are discovery coverage; automation is QA/review; monitoring is site health.

How To Use It

Human-Only UI

import { getVisitorOrNull } from '@clickstreamhq/signals';

const visitor = await getVisitorOrNull();

if (visitor && (
  !visitor.bot.isBot &&
  visitor.behavioralClass === 'human' &&
  visitor.scores.conversionReadiness >= 70
)) {
  showHumanOnlyAction();
}

AI/Search Coverage

const isDiscoveryAgent =
  visitor.bot.category === 'ai_agent' ||
  visitor.bot.category === 'search_crawler';

if (isDiscoveryAgent) {
  addStructuredDataForMachineReaders();
}

For non-JavaScript crawlers and answer agents, install Edge capture. Page code only runs when the requesting client executes JavaScript.

Automation And QA

if (visitor.bot.category === 'automation') {
  window.dispatchEvent(new CustomEvent('clickstream:qa-coverage', {
    detail: {
      page: location.pathname,
      sessionId: visitor.session.sessionId,
      score: visitor.bot.score,
    },
  }));
}

This lets test traffic prove coverage without being treated as human demand.

Signals Feed

ws.addEventListener('message', (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type !== 'event') return;

  if (msg.behavioralClass === 'human' && (msg.scores?.intent ?? 0) >= 70) {
    forwardToSupportWorkflow(msg);
  }

  if (msg.bot?.category === 'automation') {
    forwardToQaCoverage(msg);
  }
});

See Signals Feed for the full subscriber pattern.

What Not To Do

In The Dashboard

Intelligence -> Human + AI Traffic groups traffic into the same lanes used by the Signals API. Use it to answer:

Signals coverage proof turns those same labels into coverage proof, lane separation, and AI/search coverage gaps.

See Also