Contacts
Identified visitors with metadata and cross-device conversation history.
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, mobile, or a reinstalled app 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. Use a stable ID from your own user table, such as user.id, so Cossistant can recognize the same person after browser storage is cleared, an app is reinstalled, or the user signs in on another device.
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 also covers storage-loss cases. If a user reinstalls your app or loses browser storage, Cossistant may create a new visitor at first. As soon as you identify that visitor with the same externalId or email, Cossistant links them to the existing contact and restores access to that contact's prior conversations.
You do not need to persist Cossistant's visitor ID in your own user records for identified users. Store and send your stable externalId instead. Anonymous visitors remain scoped to the current device/browser storage until they are identified.
This provides seamless support across devices and reinstalls - 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