AgentShield

User Identification

Track and identify authenticated users across AI agent sessions

Overview

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

The AgentShield User Identification feature allows you to associate authenticated users on your website with their AI agent sessions. This is particularly valuable for tracking when users interact with your site through ChatGPT's agent mode, Perplexity, or other AI assistants while logged into their accounts on your website.

Why User Identification?

Use Cases

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

Benefits

  • Track the complete user journey from AI agent discovery to conversion
  • Understand how authenticated users interact with AI agents
  • Segment AI traffic by user properties (plan, company, role, etc.)
  • Build accurate conversion funnels that include AI touchpoints

How It Works

Here's the typical workflow:

  1. User visits your website with both AgentShield pixel and your analytics (Amplitude, GA4, etc.)
  2. User logs in to your website → Your analytics identifies them as userId: "abc123" with email "visitor@gmail.com"
  3. You sync the identification → Call AgentShield.identify("abc123", { email: "visitor@gmail.com" })
  4. AI agent visits your website → AgentShield tracks it with the same userId: "abc123"
  5. You see in AgentShield dashboard → The detection shows userId: "abc123" and email: "visitor@gmail.com"

Expected Result: The same user shows up with the same ID in both your analytics and AgentShield, allowing you to correlate AI traffic with your existing user data.

Implementation

Basic Usage

For traditional websites, WordPress, Shopify, and non-framework sites:

// When user logs in
function handleUserLogin(user) {
  var attempts = 0;
  var maxAttempts = 50; // Try for up to 5 seconds

  // Wait for AgentShield to load before identifying
  function identifyUser() {
    if (window.AgentShield) {
      window.AgentShield.identify(user.id, {
        email: user.email,
        name: user.name,
        plan: user.subscription_plan,
        company: user.company_name
      });
      console.log('User identified:', user.id);
    } else if (attempts < maxAttempts) {
      // Pixel not loaded yet, retry after 100ms
      attempts++;
      setTimeout(identifyUser, 100);
    } else {
      console.error('AgentShield failed to load after 5 seconds');
    }
  }

  identifyUser();
}

// When user logs out
function handleUserLogout() {
  if (window.AgentShield) {
    window.AgentShield.reset();
    console.log('User identification reset');
  }
}

Important: Always check if window.AgentShield exists before calling methods. The pixel script loads asynchronously.

For Next.js apps with NextAuth, create a component to handle identification:

// app/components/analytics-provider.tsx
'use client';

import { useEffect, useRef } from 'react';
import { useSession } from 'next-auth/react';

// TypeScript type definitions
declare global {
  interface Window {
    AgentShield?: {
      identify: (userId: string, traits?: Record<string, any>) => void;
      reset: () => void;
      getUser: () => { id: string } | null;
      track: (event: string, data?: Record<string, any>) => void;
    };
  }
}

export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
  const { data: session, status } = useSession();
  const lastUserIdRef = useRef<string | null>(null);

  useEffect(() => {
    // Wait for session to load
    if (status === 'loading') return;

    if (status === 'authenticated' && session?.user?.id) {
      const userId = session.user.id;

      // Only identify if user has changed (prevents duplicate calls)
      if (userId !== lastUserIdRef.current) {
        identifyUser(userId, {
          email: session.user.email,
          name: session.user.name,
        });
        lastUserIdRef.current = userId;
      }
    } else if (status === 'unauthenticated') {
      // Reset on logout (only if we had a previous user)
      if (lastUserIdRef.current && typeof window !== 'undefined' && window.AgentShield) {
        window.AgentShield.reset();
        lastUserIdRef.current = null;
      }
    }
  }, [session, status]);

  return <>{children}</>;
}

// Helper function with retry logic
function identifyUser(userId: string, traits?: Record<string, any>) {
  // SSR safety check
  if (typeof window === 'undefined') return;

  let attempts = 0;
  const maxAttempts = 50; // 50 seconds max wait time

  const tryIdentify = () => {
    if (window.AgentShield) {
      try {
        window.AgentShield.identify(userId, traits);
        console.log('[AgentShield] User identified:', userId);
      } catch (error) {
        console.error('[AgentShield] Identification failed:', error);
      }
    } else if (attempts < maxAttempts) {
      // Pixel not loaded yet, retry after delay
      attempts++;
      setTimeout(tryIdentify, 1000);
    } else {
      console.error('[AgentShield] Failed to load after 50 seconds');
    }
  };

  tryIdentify();
}

Then add it to your layout:

// app/layout.tsx
import Script from 'next/script';
import { AnalyticsProvider } from './components/analytics-provider';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Script
          src="https://kya.vouched.id/pixel.js"
          data-project-id="YOUR_PROJECT_ID"
          strategy="afterInteractive"
        />
        <AnalyticsProvider>
          {children}
        </AnalyticsProvider>
      </body>
    </html>
  );
}

Best Practices Used:

  • ✅ SSR safety check (typeof window === 'undefined')
  • ✅ Retry logic for async pixel loading
  • ✅ Deduplication via refs (prevents duplicate identify calls)
  • ✅ Error handling
  • ✅ Proper cleanup on logout
// components/UserIdentification.jsx
import { useEffect } from 'react';
import { useAuth } from './auth-context';

export function UserIdentification() {
  const { user, isAuthenticated } = useAuth();

  useEffect(() => {
    if (isAuthenticated && user) {
      let attempts = 0;
      const maxAttempts = 50; // Try for up to 5 seconds

      const tryIdentify = () => {
        if (window.AgentShield) {
          window.AgentShield.identify(user.id, {
            email: user.email,
            name: user.displayName,
            plan: user.subscription,
            registeredAt: user.createdAt,
          });
        } else if (attempts < maxAttempts) {
          attempts++;
          setTimeout(tryIdentify, 100);
        } else {
          console.error('AgentShield failed to load after 5 seconds');
        }
      };

      tryIdentify();
    } else if (!isAuthenticated && window.AgentShield) {
      // Reset on logout
      window.AgentShield.reset();
    }
  }, [user, isAuthenticated]);

  return null;
}

Integration with Analytics Platforms

Amplitude Integration

Sync user identification between AgentShield and Amplitude for unified analytics:

// Initialize both libraries
import * as amplitude from '@amplitude/analytics-browser';

// When user logs in
function identifyUser(user) {
  const userId = user.id;
  const userTraits = {
    email: user.email,
    name: user.name,
    plan: user.plan,
    company: user.company,
  };

  // Identify with AgentShield
  if (window.AgentShield) {
    window.AgentShield.identify(userId, userTraits);
  }

  // Identify with Amplitude
  amplitude.identify(userId, userTraits);

  // Track login event
  amplitude.track('User Logged In', {
    source: 'web',
    method: user.authMethod,
  });
}

// Listen for AgentShield identification events
window.addEventListener('agentshield:identify', (event) => {
  // Sync with Amplitude when AgentShield identifies a user
  const { userId, traits } = event.detail;
  amplitude.identify(userId, traits);
});

Google Tag Manager Integration

Push user identification to GTM's data layer:

// Enhanced identification with GTM
function identifyUserWithGTM(user) {
  // Identify with AgentShield
  if (window.AgentShield) {
    window.AgentShield.identify(user.id, {
      email: user.email,
      name: user.name,
      plan: user.plan,
    });
  }

  // Push to GTM data layer
  window.dataLayer = window.dataLayer || [];
  window.dataLayer.push({
    event: 'user_identified',
    user_id: user.id,
    user_properties: {
      email: user.email,
      name: user.name,
      plan: user.plan,
      identified_via: 'agentshield',
    },
  });
}

// Listen for AgentShield events
window.addEventListener('agentshield:identify', (event) => {
  window.dataLayer.push({
    event: 'agentshield_user_identified',
    ...event.detail,
  });
});

API Reference

window.AgentShield.identify(userId, traits)

Identifies a user and associates them with the current session.

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

Example:

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

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


window.AgentShield.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.AgentShield.getUser();
if (user) {
  console.log('Current user:', user.id);
} else {
  console.log('No user identified');
}

window.AgentShield.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.AgentShield.reset();
console.log('User identification cleared');

window.AgentShield.getSession()

Returns current session information including userId if identified.

Returns:

{
  id: 'session_uuid',
  startTime: 1234567890,
  duration: 30000, // milliseconds since session start
  userId: 'user_123' // or null if not identified
}

Example:

const session = window.AgentShield.getSession();
console.log('Session duration:', session.duration, 'ms');
console.log('User ID:', session.userId || 'anonymous');

window.AgentShield.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.AgentShield.track('form_submit', {
  form_id: 'contact-form',
  page: window.location.pathname,
});

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

window.AgentShield.grantConsent()

Grants consent for cookie storage (GDPR compliance). Call this after obtaining user consent.

Example:

// After user accepts cookie consent
if (userAcceptedCookies) {
  window.AgentShield.grantConsent();
}

If you initialize the pixel with data-require-consent="true", user IDs will only be stored in memory until grantConsent() is called.

Events

agentshield:identify

Fired when a user is identified. Useful for syncing with other analytics tools.

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

agentshield:reset

Fired when user identification is reset (logout).

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

Testing with AI Agents

ChatGPT Agent Mode Testing

To test user identification with ChatGPT's agent mode, you'll need to implement a login flow that ChatGPT can navigate.

  1. Implement a test login endpoint:
// pages/api/test-login.js (Next.js example)
export default function handler(req, res) {
  // Simple test authentication
  if (req.method === 'POST') {
    const { username, password } = req.body;

    if (username === 'test' && password === 'test123') {
      // Set session/cookie
      res.status(200).json({
        success: true,
        user: {
          id: 'test_user_123',
          email: 'test@example.com',
          name: 'Test User',
          plan: 'premium',
        },
      });
    }
  }
}
  1. Create a login page ChatGPT can navigate:
<!-- public/login.html -->
<form id="login-form">
  <input type="text" id="username" placeholder="Username" />
  <input type="password" id="password" placeholder="Password" />
  <button type="submit">Login</button>
</form>

<script>
  document.getElementById('login-form').onsubmit = async (e) => {
    e.preventDefault();

    const response = await fetch('/api/test-login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        username: document.getElementById('username').value,
        password: document.getElementById('password').value,
      }),
    });

    if (response.ok) {
      const { user } = await response.json();

      // Identify with AgentShield
      if (window.AgentShield) {
        window.AgentShield.identify(user.id, {
          email: user.email,
          name: user.name,
          plan: user.plan,
        });

        console.log('User identified:', user.id);
        alert('Login successful! User identified.');
      }
    }
  };
</script>
  1. Test flow with ChatGPT:
    • Ask ChatGPT: "Visit [your-site-url] and log in with username 'test' and password 'test123'"
    • ChatGPT will navigate to your site (tracked as anonymous session)
    • ChatGPT will fill in the login form
    • Upon successful login, the user will be identified
    • Check your AgentShield dashboard to see the userId associated with the ChatGPT session

Privacy and Security

Important Security Considerations: - Never send sensitive information (passwords, SSNs, etc.) as user traits - User traits are stored in your database and may be visible in logs - Ensure you comply with GDPR/CCPA when tracking user data - Implement proper authentication before calling identify()

Best Practices

  1. Verify authentication server-side before identifying users
  2. Use consistent user IDs across all your analytics platforms
  3. Include relevant traits that help with segmentation and analysis
  4. Call reset() on logout to properly end the identified session
  5. Test thoroughly with both human users and AI agents

Troubleshooting

window.AgentShield is undefined

Cause: The pixel script hasn't loaded yet when you're trying to call identify().

Solution: Use retry logic:

function waitForAgentShield(callback, maxRetries = 50) {
  if (window.AgentShield) {
    callback();
  } else if (maxRetries > 0) {
    setTimeout(() => waitForAgentShield(callback, maxRetries - 1), 100);
  } else {
    console.error('AgentShield failed to load after 5 seconds');
  }
}

// Usage
waitForAgentShield(() => {
  window.AgentShield.identify('user_123', { email: 'user@example.com' });
});

User not showing in dashboard

Troubleshooting steps:

  1. Check pixel is loaded:

    console.log('Pixel loaded:', typeof window.AgentShield !== 'undefined');
    console.log('Init info:', window.AgentShield?.getInitInfo());
  2. Verify identification was called:

    console.log('Current user:', window.AgentShield?.getUser());
  3. Check browser console for errors:

    • Look for CSP violations
    • Look for network errors to kya.vouched.id
    • Look for JavaScript errors
  4. Verify project ID matches your dashboard

  5. Wait 1-2 minutes - Data can take time to appear


Duplicate identify() calls

Cause: Identify is being called multiple times on the same page/session.

Solution: Use deduplication pattern:

let lastIdentifiedUser = null;

function identifyIfChanged(userId, traits) {
  if (userId !== lastIdentifiedUser) {
    window.AgentShield?.identify(userId, traits);
    lastIdentifiedUser = userId;
  }
}

Or in React/Next.js, use useRef:

const lastUserIdRef = useRef(null);

if (userId !== lastUserIdRef.current) {
  window.AgentShield?.identify(userId, traits);
  lastUserIdRef.current = userId;
}

Session not persisting across page reloads

Causes:

  • Cookies are blocked
  • Third-party cookie restrictions
  • Incognito/private browsing mode

Solutions:

  1. Check cookies are enabled:

    console.log('Cookies enabled:', navigator.cookieEnabled);
  2. Check for cookie consent - If you're using data-require-consent="true", make sure to call:

    window.AgentShield.grantConsent();
  3. Inspect cookies in DevTools - Look for agentshield_user cookie


TypeScript errors

Error: Property 'AgentShield' does not exist on type 'Window'

Solution: Add type declarations:

declare global {
  interface Window {
    AgentShield?: {
      identify: (userId: string, traits?: Record<string, any>) => void;
      reset: () => void;
      getUser: () => { id: string } | null;
      getSession: () => { id: string; startTime: number; duration: number; userId: string | null };
      track: (event: string, data?: Record<string, any>) => void;
      grantConsent: () => void;
      getInitInfo: () => object;
    };
  }
}

Next.js "window is not defined" error

Cause: Code is running on the server during SSR.

Solution: Add SSR safety check:

if (typeof window !== 'undefined' && window.AgentShield) {
  window.AgentShield.identify(userId, traits);
}

Or use 'use client' directive in components that use window.AgentShield.

Example: Complete Next.js Integration

Here's a complete example integrating AgentShield with Amplitude in a Next.js app:

// app/providers/analytics-provider.tsx
'use client';

import { useEffect } from 'react';
import { useSession } from 'next-auth/react';
import * as amplitude from '@amplitude/analytics-browser';

const AMPLITUDE_API_KEY = process.env.NEXT_PUBLIC_AMPLITUDE_API_KEY!;

export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
  const { data: session, status } = useSession();

  useEffect(() => {
    // Initialize Amplitude
    amplitude.init(AMPLITUDE_API_KEY);

    // Set up AgentShield event listeners
    const handleIdentify = (event: CustomEvent) => {
      const { userId, traits } = event.detail;
      amplitude.identify(userId, traits);
      amplitude.track('User Identified via AgentShield', {
        sessionId: event.detail.sessionId
      });
    };

    const handleReset = () => {
      amplitude.reset();
    };

    window.addEventListener('agentshield:identify', handleIdentify as EventListener);
    window.addEventListener('agentshield:reset', handleReset);

    return () => {
      window.removeEventListener('agentshield:identify', handleIdentify as EventListener);
      window.removeEventListener('agentshield:reset', handleReset);
    };
  }, []);

  useEffect(() => {
    if (status === 'authenticated' && session?.user) {
      const userId = session.user.id;
      const traits = {
        email: session.user.email,
        name: session.user.name,
      };

      // Identify with both platforms
      if (window.AgentShield) {
        window.AgentShield.identify(userId, traits);
      }
      amplitude.identify(userId, traits);

    } else if (status === 'unauthenticated') {
      // Reset both platforms
      if (window.AgentShield) {
        window.AgentShield.reset();
      }
      amplitude.reset();
    }
  }, [session, status]);

  return <>{children}</>;
}

This completes the user identification feature, allowing you to track authenticated users across AI agent sessions!

Command Palette

Search for a command to run...