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);
}
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 matchesnull
- Timeout or error occurred
Always check for null
before accessing array properties.
Best Practices
- Configure appropriate timeout: 200-500ms balances accuracy and performance
- Use progressive enhancement: Render default experience first, then personalize
- Handle errors gracefully: Always provide fallback experiences when ICP detection fails
- Leverage caching: Use
cacheOnly
option for instant subsequent page loads - Test thoroughly: Verify behavior across different visitor types and network conditions