Marketing Pixel

Lightweight, no-code AI agent detection for analytics and marketing teams

Overview

The Checkpoint Marketing Pixel is a lightweight, no-code detection snippet that identifies AI agents and bots visiting your website. It loads asynchronously, has minimal impact on page performance, and installs via a simple script tag or your tag manager.

The Pixel is ideal for:

  • Marketing teams who want bot traffic visibility without developer involvement
  • Analytics teams who need to separate real users from automated traffic
  • Content teams who want to monitor AI scraping activity

The pixel registers its JavaScript API on window.Checkpoint and stores its device-session id in the checkpoint_user cookie.

Prerequisites

You'll need your Project ID before installing the Pixel: see Credentials for where to find it under Installations in your project dashboard.

Installation

Add the following script tag in your HTML <head> section:

<!-- Detect Pixel -->
<script>
  (function () {
    var as = document.createElement('script');
    as.type = 'text/javascript';
    as.async = true;
    as.src = 'https://kya.vouched.id/pixel.js';
    as.setAttribute('data-project-id', 'YOUR_PROJECT_ID');
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(as, s);
  })();
</script>
<!-- End Detect Pixel -->

Replace YOUR_PROJECT_ID with your Project ID (see Credentials). This is the exact snippet the dashboard generates for you.

How It Works

  1. The Pixel script loads asynchronously after your page renders
  2. It collects detection signals (user agent, headers, behavior, and, when enabled, a browser fingerprint)
  3. Signals are POSTed to the Checkpoint detection API (POST /api/v1/pixel)
  4. The result (classification + confidence) is logged to your project
  5. View results in the dashboard

The Pixel never blocks page rendering and adds no perceptible latency to the user experience.

Configuration

Project ID

Every Pixel installation requires a Project ID, set via data-project-id. See Credentials for where to find it.

Script attributes

All options are set as data-* attributes on the script tag:

AttributeDefaultDescription
data-project-idRequiredYour Checkpoint Project ID
data-debugfalse"true" enables verbose console logging
data-api-endpoint<origin>/api/v1/pixelOverride the ingestion endpoint
data-session-timeout1800000Session timeout in ms (30 minutes)
data-respect-dnttrueHonor the browser Do Not Track signal ("false" to disable)
data-batch-size10Events per batch
data-flush-interval5000Batch send interval (ms)
data-enable-fingerprintingtrueCollect a browser fingerprint ("false" to disable)
data-require-consentfalseGDPR: defer cookie storage until grantConsent() is called
data-anonymize-ipfalse"true" truncates the IP (IPv4 /24, IPv6 /48) before storage
data-strip-fragmenttrueStrip the #fragment from url/referrer ("false" to disable)
data-scrub-paramsnoneComma-separated query param names to redact to "[redacted]" in url/referrer
data-drop-queryfalse"true" drops the whole query string from url/referrer

To send events through your own CDN instead of straight to Checkpoint, point data-api-endpoint at a path your edge proxies. See Forward edge evidence.

data-anonymize-ip defaults to false for both the pixel and the Beacon: Tier 2 vendor-IP corroboration (matching a request's IP against a known AI vendor's published range) needs the full address. Turning this on trades that corroboration accuracy for privacy; only enable it if you don't need Tier 2 attribution.

What data leaves the page. Every pageview sends the current page's url (including its query string by default), document.referrer, and document.title, plus the standard browser/device signals (user agent, screen size, timezone, and, when enabled, a fingerprint). Redaction runs client-side, before any of it is collected: data-strip-fragment (on by default) strips the #fragment: never sent to the server by any other web surface, and the highest-risk part of a URL for tokens (SPA routers, magic links, OAuth implicit flow). data-scrub-params and data-drop-query are off by default; set one if your site puts tokens or PII in its query strings. title has no redaction option: avoid putting sensitive values in <title> if that's a concern.

Subresource Integrity (SRI)

Every pixel build also publishes a content-addressed, versioned path (/pixel/<version>.js) alongside the default /pixel.js. Pin the version and its integrity hash if you want the browser itself to refuse a tampered file, rather than trusting whatever the CDN currently serves at /pixel.js:

<script
  src="https://kya.vouched.id/pixel/VERSION.js"
  integrity="sha384-INTEGRITY_HASH"
  crossorigin="anonymous"
  data-project-id="YOUR_PROJECT_ID"
></script>

Find the current VERSION and INTEGRITY_HASH in your dashboard's installation snippet, or by fetching /pixel-integrity.json. A versioned URL is immutable: a new pixel build always publishes a new version rather than overwriting an old one, so pinning one never silently changes under you; upgrade by re-copying the snippet.

Custom Events

Send custom events with the window.Checkpoint.track method:

<script>
  // Guard on the global: calls before pixel.js loads are lost (there is no queue)
  window.Checkpoint &&
    window.Checkpoint.track('form_submit', {
      form_id: 'contact',
      page: '/contact',
    });
</script>

JavaScript API

Once pixel.js loads, it exposes window.Checkpoint:

Method / propertyDescription
track(eventName, data?)Send a custom event
identify(userId, traits?)Associate a user id (traits are sent server-side only, never stored in the cookie; rate-limited)
getUser()Returns { id } for the identified user, or null
grantConsent()Enable cookie storage after obtaining GDPR consent (see data-require-consent)
reset()Clear identity + cookie and start a fresh anonymous session (logout / GDPR)
getSession()Returns { id, startTime, duration, userId }
getInitInfo()Returns version/init diagnostics (useful for duplicate-load debugging)
lastDetectionThe most recent agent detection object. Unset until an agent is detected: human traffic never populates it

window.Checkpoint.identify(userId, traits)

Identifies a user and associates them with the current session. Common uses:

  • Unified Analytics: Sync user data between Checkpoint, Google Analytics, Amplitude, and other analytics platforms
  • Personalized AI Responses: Track authenticated users' AI agent interactions
  • Session Attribution: Connect unattributed AI sessions to known users when they log in
  • Cross-Platform Tracking: Maintain user identity across different AI platforms and sessions

Important: This feature is for your customer-facing website, not the Checkpoint dashboard. The userId comes from your existing analytics (Amplitude, GA4, etc.), not from Checkpoint.

Parameters:

  • userId (string, required): Unique identifier for the user
  • traits (object, optional): Additional user properties

Important Notes:

  • User ID is stored in cookies for persistent identification (requires user consent under GDPR)
  • User traits (email, name, etc.) are sent to the server and NOT stored in cookies
  • Call this method when users log in or when you want to identify a session

identify() enforces a 1-second minimum interval between calls. A call inside that window is dropped (it returns without sending) and each successive violation doubles an internal backoff, up to 10 seconds; the counter decays as soon as an allowed call goes through. Rate limiting is skipped when data-debug="true" or on localhost, so a development run won't reproduce it.

Example:

window.Checkpoint.identify('user_123', {
  email: 'john@example.com',
  name: 'John Doe',
  plan: 'premium',
  company: 'Acme Corp',
});

Privacy Note: User traits are sent to Checkpoint servers and stored in your database. Never send sensitive information like passwords or credit card numbers.

window.Checkpoint.getUser()

Returns the currently identified user or null if no user is identified.

Returns:

{
  id: 'user_123';
}
// or null if not identified

Note: User traits are not returned by this method (they're stored server-side only).

Example:

const user = window.Checkpoint.getUser();
if (user) {
  console.log('Current user:', user.id);
} else {
  console.log('No user identified');
}

window.Checkpoint.reset()

Clears the current user identification and starts a new anonymous session. Call this when users log out.

What it does:

  • Clears user ID from cookies
  • Generates new session ID
  • Generates new device ID
  • Sends logout event to server

Example:

// On user logout
window.Checkpoint.reset();
console.log('User identification cleared');

window.Checkpoint.track(eventName, data)

Track custom events for analytics and detection.

Parameters:

  • eventName (string, required): Name of the event
  • data (object, optional): Additional event data

Example:

// Track form submission
window.Checkpoint.track('form_submit', {
  form_id: 'contact-form',
  page: window.location.pathname,
});

// Track button click
window.Checkpoint.track('button_click', {
  button: 'pricing-cta',
  plan: 'enterprise',
});

Events

The pixel also dispatches window CustomEvents you can listen for:

Eventdetail
checkpoint:detectionthe detection result (isAgent, confidence, …). Fires only when an agent is detected, not on every pageview
checkpoint:identify{ userId, traits, sessionId, deviceId }
checkpoint:reset{ previousUserId, newSessionId }
checkpoint:loaded{ version, projectId }. Fires once, as soon as the script is present, before any consent check

What about bots and humans?

checkpoint:detection and lastDetection are gated on isAgent, which is true only for interactive AI assistants: ChatGPT, Claude, Perplexity. Bots (Googlebot, GPTBot, headless browsers) and humans classify with isAgent: false, so neither fires the event nor populates lastDetection. If you wire a GA4 forward off checkpoint:detection (as shown below), you are measuring AI-assistant traffic only, not all automation.

Every classification (agent, bot, and human alike) is still sent server-side and appears in the dashboard. Client-side, the pixel also records the last result to sessionStorage regardless of class:

// Written on every detection, not just agents.
const recent = JSON.parse(sessionStorage.getItem('checkpoint_recent_detection') || 'null');
// → { isAgent: boolean, isBot: boolean, confidence: number, timestamp: number }

Read isBot there to react to crawler traffic on the client.

checkpoint:identify fires when a user is identified, which is useful for syncing with other analytics tools; checkpoint:reset fires when identification is reset (logout):

window.addEventListener('checkpoint:identify', (event) => {
  console.log('User identified:', event.detail);
  // event.detail contains: { userId, traits, sessionId, deviceId }
});

window.addEventListener('checkpoint:reset', (event) => {
  console.log('User reset:', event.detail);
  // event.detail contains: { previousUserId, newSessionId }
});

For an end-to-end walkthrough of wiring identify() into your login flow, with framework patterns for Next.js and React plus verification and troubleshooting, see the Identify Users cookbook. To identify users via Google Tag Manager, see the GTM + Next.js guide.

The pixel honors Do Not Track by default (data-respect-dnt) and auto-tracks SPA navigations (History pushState/popstate/hashchange). It stores a device/session id in the checkpoint_user cookie (deferred until grantConsent() when data-require-consent="true").

On a Shopify storefront the pixel gates itself on Shopify's Customer Privacy API. The gate engages whenever window.Shopify is present with a storefront signal (Shopify.shop, loadFeatures, or customerPrivacy); pages without window.Shopify behave exactly as described above.

While the gate is engaged:

  • Consent counts as not granted until Shopify reports analytics processing allowed, either because the buyer accepted or because consent is not required in their region.
  • data-require-consent is forced on, so no cookie is written, and every outbound event is dropped. The fingerprint detector is not loaded either. Calls to track() and identify() before consent are discarded, not queued.
  • The pixel reads the current answer on load and listens for visitorConsentCollected, so a buyer who accepts the banner later starts being collected in the same page view. A later withdrawal stops further sends.
  • When consent is granted the pixel runs grantConsent() for you, which also adopts or mints the shared _as_beacon_session cookie.
  • checkpoint:loaded still fires regardless of consent. It means only that the script is present.
  • If Shopify's consent API fails to load, the gate fails closed and nothing is sent.

You do not need to call grantConsent() yourself on Shopify. The theme app extension handles installation; see the Shopify app for setup.

Analytics Integration

Google Analytics 4

The pixel does not push to GA4 or the dataLayer on its own. Instead, listen for the checkpoint:detection event and forward it to GA4 yourself:

<script>
  window.addEventListener('checkpoint:detection', function (e) {
    var d = e.detail || {};
    // Forward to GA4 (gtag must already be installed)
    window.gtag &&
      window.gtag('event', 'checkpoint_detection', {
        is_agent: d.isAgent,
        confidence: d.confidence,
      });
  });
</script>

This lets you build GA4 audiences that exclude bot traffic and measure true conversion rates.

Pixel vs Beacon

FeaturePixelBeacon
InstallationScript tag / GTMnpm package or script tag
Code requiredNoneYes
Signal richnessBasic + fingerprintAdvanced
Event trackingtrack()trackEvent()
Web WorkerNoOptional (useWorker)
Bundle size≈5 KB gzipped, plus ≈4.5 KB for the fingerprint detector it loads when data-enable-fingerprinting is on (the default)Under 28 KB gzipped (script tag)
Best forMarketing, analyticsApplication integration

For more advanced client-side collection, see the Beacon.

Troubleshooting

Pixel Not Loading

  • Check that the Project ID is correct
  • Verify no ad blockers or content security policies are blocking the script
  • Check the browser console for errors (set data-debug="true")

No Detections in Dashboard

  • Confirm the Pixel is loading (check the Network tab for pixel.js and a POST /api/v1/pixel)
  • Verify the Project ID matches your dashboard project
  • Check that GTM is published (if using GTM)
  • If the visitor's browser sends Do Not Track, the pixel collects nothing unless you set data-respect-dnt="false"
  • On a Shopify store, nothing is sent until the buyer's consent is granted or Shopify reports that consent is not required in their region (see Shopify buyer consent)

Content Security Policy

The Pixel loads pixel.js and checkpoint-detector.js from kya.vouched.id and sends events there with fetch, so a CSP needs the host in both directives:

Content-Security-Policy: script-src 'self' https://kya.vouched.id; connect-src 'self' https://kya.vouched.id;

If you set data-api-endpoint, allow that host in connect-src instead.

Next Steps