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:
- User visits your website with both AgentShield pixel and your analytics (Amplitude, GA4, etc.)
- User logs in to your website → Your analytics identifies them as
userId: "abc123"
with email"visitor@gmail.com"
- You sync the identification → Call
AgentShield.identify("abc123", { email: "visitor@gmail.com" })
- AI agent visits your website → AgentShield tracks it with the same
userId: "abc123"
- You see in AgentShield dashboard → The detection shows
userId: "abc123"
andemail: "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 usertraits
(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 eventdata
(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.
- 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',
},
});
}
}
}
- 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>
- 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
- Verify authentication server-side before identifying users
- Use consistent user IDs across all your analytics platforms
- Include relevant traits that help with segmentation and analysis
- Call reset() on logout to properly end the identified session
- 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:
-
Check pixel is loaded:
console.log('Pixel loaded:', typeof window.AgentShield !== 'undefined'); console.log('Init info:', window.AgentShield?.getInitInfo());
-
Verify identification was called:
console.log('Current user:', window.AgentShield?.getUser());
-
Check browser console for errors:
- Look for CSP violations
- Look for network errors to
kya.vouched.id
- Look for JavaScript errors
-
Verify project ID matches your dashboard
-
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:
-
Check cookies are enabled:
console.log('Cookies enabled:', navigator.cookieEnabled);
-
Check for cookie consent - If you're using
data-require-consent="true"
, make sure to call:window.AgentShield.grantConsent();
-
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!