What are Contacts?
Contacts are identified visitors. When you identify an anonymous visitor with an externalId (your user ID) or email, they become a contact.
Contacts enable:
- Cross-device support: Same user on desktop and mobile shares conversation history
- Rich metadata: Attach context like plan type, MRR, company, or lifecycle stage
- Dashboard visibility: Support agents see user details alongside conversations
- Persistent identity: Conversations follow the user, not the device
Creating Contacts
Identification Requirements
A contact requires at least one of:
externalId: Your internal user ID (recommended)email: User's email address
Both are accepted, but externalId is preferred for robust cross-system tracking.
Using the Component
import { IdentifySupportVisitor } from "@cossistant/next";
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
export default async function DashboardLayout({ children }) {
const session = await auth.api.getSession({
headers: await headers(),
});
return (
<div>
{session?.user && (
<IdentifySupportVisitor
externalId={session.user.id}
email={session.user.email}
name={session.user.name}
image={session.user.image}
metadata={{
plan: session.user.plan,
signupDate: session.user.createdAt,
company: session.user.company,
mrr: session.user.mrr,
}}
/>
)}
{children}
</div>
);
}Using the Hook
"use client";
import { useVisitor } from "@cossistant/next";
import { useEffect } from "react";
export function AuthHandler({ user }) {
const { visitor, identify } = useVisitor();
useEffect(() => {
if (user && !visitor?.contact) {
identify({
externalId: user.id,
email: user.email,
name: user.name,
image: user.avatar,
metadata: {
plan: user.plan,
signupDate: user.createdAt,
},
});
}
}, [user, visitor?.contact, identify]);
return null;
}Contact Metadata
Metadata provides context to support agents during conversations. It appears in your dashboard alongside chat threads.
What to Include
Common metadata fields:
- plan: Subscription tier (free, pro, enterprise)
- signupDate: When the user joined
- company: Organization name
- mrr: Monthly recurring revenue
- lifecycleStage: lead, trial, customer, churned
- lastActive: Last activity timestamp
Metadata Schema
type VisitorMetadata = Record<string, string | number | boolean | null>;Only primitive values are supported—no nested objects or arrays.
Updating Metadata
Metadata can be updated anytime to reflect user changes:
"use client";
import { useVisitor } from "@cossistant/next";
export function UpgradeButton() {
const { setVisitorMetadata } = useVisitor();
const handleUpgrade = async () => {
await upgradeToPro();
// Update metadata so agents see the new plan
await setVisitorMetadata({
plan: "pro",
upgradedAt: new Date().toISOString(),
mrr: 99,
});
};
return <button onClick={handleUpgrade}>Upgrade to Pro</button>;
}Efficient Metadata Updates
Cossistant hashes metadata before sending updates. If metadata hasn't changed, no API call is made—preventing unnecessary network requests and database writes.
This means you can safely call setVisitorMetadata() or re-render <IdentifySupportVisitor /> without performance concerns.
One Contact, Multiple Visitors
A single contact can have multiple visitors associated with it:
- Desktop visitor: User on their laptop
- Mobile visitor: Same user on their phone
- Tablet visitor: Same user on their iPad
All three visitors share:
- Conversation history
- Contact metadata
- Support context
This provides seamless support across devices—agents see the full picture regardless of where the user reaches out.
Learn More
- Visitors: Anonymous users before identification
- IdentifySupportVisitor: Component for creating contacts
- useVisitor: Hook for programmatic contact management
- Conversations: Chat threads associated with contacts