Skip to main content

Real-time ICP

Syft enables real-time ICP (Ideal Customer Profile) detection for all website visitors within milliseconds of page load, enabling dynamic personalization for high-value prospects without requiring email identification.

Configuration

Initialize the SDK with real-time ID detection enabled:

syft.init({
sourceId: "${apiKey}",
enableRealTimeID: true, // Enable real-time ICP detection
realTimeIDTimeout: 500 // Optional Timeout in milliseconds (default: 2000ms)
});

Data Retrieval

Use fetchID() to retrieve ICP classifications for any visitor. Syft automatically uses all available information to identify the visitor, including IP address, cookies, and email (if previously identified). This method works for both anonymous and identified visitors.

// Returns Promise<{ icps: string[] | null; error: string }>
const { icps, error } = await syft.fetchID(options);

// Options (all optional):
{
timeout: 100, // Override default timeout (ms)
cacheOnly: true, // Force cache read only (returns immediately)
skipCache: true // Force remote fetch (bypass cache)
}

Usage Examples:

// Default behavior
const { icps, error } = await syft.fetchID();

// Custom timeout
const { icps, error } = await syft.fetchID({ timeout: 100 });

// Instant cache check. when low latency is important over accuracy.
const { icps, error } = await syft.fetchID({ cacheOnly: true });

// Force fresh data
const { icps, error } = await syft.fetchID({ skipCache: true });

Possible responses:

  • Matched ICPs: ["Enterprise", "High-Growth-SaaS", "Fortune-500"]
  • No matches: []
  • Error/timeout/cache miss: null

identify Method

As an alternative to calling fetchID() separately, the identify() method returns the same ICP data as part of the identification process. This is convenient when you're identifying a user and want to get their ICPs in a single call. More info about identify call is here.

// Returns Promise<{ icps: string[] | null; error: string }>
const { icps, error } = await syft.identify("visitor@example.com", {
source: "signup",
firstName: "John",
lastName: "Doe"
});

if (error) {
console.error('Identification failed:', error);
} else if (icps && icps.length > 0) {
console.log('Visitor ICPs:', icps);
// Apply personalization based on identified visitor's ICPs
applyPersonalization(icps);
}
ICP Updates After Identification

When a visitor is identified through the identify() method, their ICP classification may be updated based on the newly provided enrichment data. Future calls to fetchID() will use this enriched profile for more accurate ICP matching.

Implementation Examples

Basic JavaScript Implementation

// Initialize visitor experience with ICP detection using primary fetchID() method
async function initializeVisitorExp() {
const { icps, error } = await syft.fetchID();

if (error) {
console.error('Syft ICP detection failed:', error);
} else if (icps && icps.length > 0) {
activateEnterpriseExperience(icps);
}
}

// Example: Handle user signup with two approaches
async function handleUserSignup(email, userInfo) {
// Approach 1: Use identify() to get ICPs in single call
const identifyResult = await syft.identify(email, {
source: "signup",
...userInfo
});

if (identifyResult.icps) {
// Use ICPs returned from identify()
activateEnterpriseExperience(identifyResult.icps);
}

// Approach 2: Alternative - identify then use fetchID()
// await syft.identify(email, { source: "signup", ...userInfo });
// const fetchResult = await syft.fetchID(); // Now has enriched data
// if (fetchResult.icps) activateEnterpriseExperience(fetchResult.icps);
}

// Activate enterprise UI elements
function activateEnterpriseExperience(icpList) {
// Show enterprise CTA in header
if (icpList.includes("Enterprise") || icpList.includes("Fortune-500")) {
const headerCTA = document.createElement('button');
headerCTA.className = 'enterprise-header-cta';
headerCTA.textContent = 'Request Enterprise Demo';
headerCTA.onclick = () => window.location.href = '/enterprise-demo';

const header = document.querySelector('.main-header');
if (header) {
header.appendChild(headerCTA);
}
}

// Activate chat widget for qualified ICPs
if (icpList.some(icp => ["Enterprise", "High-Growth-SaaS", "Mid-Market"].includes(icp))) {
if (window.Intercom) {
window.Intercom('update', {
customAttributes: {
visitor_type: 'ICP',
show_sales_bot: true
}
});
}
}
}

// Execute on page load
document.addEventListener('DOMContentLoaded', initializeVisitorExp);

React Implementation

import { useEffect, useState } from 'react';

// Custom hook for ICP detection using fetchID as primary method
function useICPDetection() {
const [icps, setICPs] = useState(null);
const [loading, setLoading] = useState(true);

const fetchICPs = async () => {
try {
// fetchID() works for both anonymous and identified visitors
const result = await syft.fetchID();
if (result.error) {
console.error('Syft ICP detection failed:', result.error);
} else {
setICPs(result.icps);
}
setLoading(false);
} catch (error) {
console.error('ICP detection error:', error);
setICPs(null);
setLoading(false);
}
};

useEffect(() => {
fetchICPs();
}, []);

// Method to refresh ICPs (useful after identification)
const refreshICPs = () => {
setLoading(true);
fetchICPs();
};

// Method to update ICPs directly (when using identify() method)
const updateICPsFromIdentification = (newICPs) => {
setICPs(newICPs);
setLoading(false);
};

return { icps, loading, refreshICPs, updateICPsFromIdentification };
}

const qualifiedICPs = ["Enterprise", "Mid-Market", "High-Growth-SaaS"];

// Main component
function App() {
const { icps, loading, refreshICPs, updateICPsFromIdentification } = useICPDetection();
const [showChat, setShowChat] = useState(false);

useEffect(() => {
if (icps && icps.length > 0) {
if (icps.some(icp => qualifiedICPs.includes(icp))) {
setShowChat(true);
}
}
}, [icps]);

// Option 1: Use identify() method to get ICPs in single call
const handleUserIdentificationWithICPs = async (email, userInfo) => {
try {
const result = await syft.identify(email, {
source: "signup",
...userInfo
});

if (result.icps) {
// Update ICPs directly from identify() response
updateICPsFromIdentification(result.icps);
}
} catch (error) {
console.error('User identification failed:', error);
}
};

// Option 2: Identify user then refresh ICPs with fetchID()
const handleUserIdentificationWithRefresh = async (email, userInfo) => {
try {
await syft.identify(email, {
source: "signup",
...userInfo
});

// Refresh ICPs using fetchID() which now has enriched data
refreshICPs();
} catch (error) {
console.error('User identification failed:', error);
}
};

const isEnterprise = icps?.includes("Enterprise") || false;

return (
<>
<Header
showEnterpriseCTA={isEnterprise}
onSignup={handleUserIdentificationWithICPs}
/>
{showChat && <ChatWidget />}
</>
);
}

// Header with conditional CTA
function Header({ showEnterpriseCTA, onSignup }) {
return (
<header className="main-header">
{showEnterpriseCTA && (
<button className="enterprise-header-cta">
Request Enterprise Demo
</button>
)}
<button onClick={() => onSignup('user@example.com', { firstName: 'John' })}>
Sign Up
</button>
</header>
);
}

// Chat widget
function ChatWidget() {}

Error Handling

The API returns an error field for timeouts/errors:

  • ["Enterprise", "Mid-Market"] - Visitor matched ICPs
  • [] - No ICP matches
  • null - Timeout or error occurred

Always check for null before accessing array properties.

Best Practices

  1. Configure appropriate timeout: 200-500ms balances accuracy and performance
  2. Use progressive enhancement: Render default experience first, then personalize
  3. Handle errors gracefully: Always provide fallback experiences when ICP detection fails
  4. Leverage caching: Use cacheOnly option for instant subsequent page loads
  5. Test thoroughly: Verify behavior across different visitor types and network conditions