Skip to main content

identify()

Identifies a user and updates their attributes in the CDP.

Signature​

async identify(
identifier: string,
properties?: Record<string, any>
): Promise<void>

Parameters​

identifier​

  • Type: string
  • Required: Yes
  • Description: Unique identifier for the user (typically your internal user ID)

The identifier must not be empty or whitespace-only.

properties​

  • Type: Record<string, any>
  • Required: No
  • Default: {}
  • Description: Custom attributes to associate with the user

Properties can include any JSON-serializable values:

  • Strings, numbers, booleans
  • Dates (as ISO strings)
  • Nested objects
  • Arrays

Person Attributes​

The Conviso automatically recognizes firstName/first_name, lastName/last_name, and email as special person attributes. See the Person Attributes section for more information on which traits are recommended.

Returns​

Promise<void> - Resolves when the user is identified successfully.

Throws​

  • Error: If the identifier is empty or invalid
  • Network errors: If the request fails

Behavior​

The identify() method:

  1. Validates the identifier
  2. Normalizes the properties
  3. Sends data to the Conviso
  4. (If enabled) Also sends data to Customer.io for dual-write

Usage Examples​

Basic Identification​

await client.identify('user123', {
email: '[email protected]',
firstName: 'John',
lastName: 'Doe'
});

With Rich Attributes​

await client.identify('user123', {
email: '[email protected]',
firstName: 'John',
lastName: 'Doe',
plan: 'premium',
created_at: new Date().toISOString(),
age: 32,
company: {
name: 'Acme Inc',
size: '50-100',
industry: 'Technology'
},
tags: ['vip', 'early-adopter'],
preferences: {
newsletter: true,
notifications: {
email: true,
push: false
}
}
});

Update User Attributes​

You can call identify() multiple times for the same user to update their attributes:

// Initial identification
await client.identify('user123', {
email: '[email protected]',
firstName: 'John',
lastName: 'Doe',
plan: 'free'
});

// Later: upgrade to premium
await client.identify('user123', {
plan: 'premium',
upgraded_at: new Date().toISOString()
});
Attribute Merging

The Conviso merges new attributes with existing ones. You don't need to send all attributes every time—only the ones you want to update.

Identify on User Signup​

app.post('/api/signup', async (req, res) => {
const { email, firstName, lastName, password } = req.body;

try {
// Create user in your database
const user = await db.users.create({
email,
firstName,
lastName,
passwordHash: await hash(password)
});

// Identify in Conviso with person attributes
await client.identify(user.id, {
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
created_at: user.createdAt.toISOString(),
source: 'web_signup'
});

res.json({ success: true });
} catch (error) {
res.status(500).json({ error: error.message });
}
});

Identify on User Login​

app.post('/api/login', async (req, res) => {
const { email, password } = req.body;

try {
const user = await db.users.findByEmail(email);

if (!user || !await verifyPassword(password, user.passwordHash)) {
return res.status(401).json({ error: 'Invalid credentials' });
}

// Update last login
await client.identify(user.id, {
last_login_at: new Date().toISOString(),
login_count: user.loginCount + 1
});

res.json({ success: true, token: generateToken(user) });
} catch (error) {
res.status(500).json({ error: error.message });
}
});

Batch Identification​

When identifying multiple users, the SDK automatically handles concurrency:

const users = await db.users.findAll();

// The SDK will limit concurrency automatically based on maxConcurrentRequests
await Promise.all(
users.map(user =>
client.identify(user.id, {
email: user.email,
name: user.name,
plan: user.plan
})
)
);

console.log(`Identified ${users.length} users`);

Validation​

The SDK validates inputs before sending requests:

// ❌ Throws: Identifier cannot be empty
await client.identify('', { email: '[email protected]' });

// ❌ Throws: Identifier cannot be empty
await client.identify(' ', { email: '[email protected]' });

// ✅ Valid
await client.identify('user123', { email: '[email protected]' });

// ✅ Valid - Properties are optional
await client.identify('user123');

Error Handling​

try {
await client.identify('user123', {
email: '[email protected]',
firstName: 'John',
lastName: 'Doe'
});
console.log('User identified successfully');
} catch (error) {
if (error.message.includes('Identifier cannot be empty')) {
console.error('Invalid identifier provided');
} else if (error.response?.status === 401) {
console.error('Invalid API key');
} else if (error.response?.status === 429) {
console.error('Rate limit exceeded');
} else {
console.error('Failed to identify user:', error.message);
}
}

Dual-Write Behavior​

When sendToCustomerIo is enabled, the SDK sends the same data to both Conviso and Customer.io:

const client = new CDPClient({
cdpApiKey: 'your-cdp-key',
sendToCustomerIo: true,
customerIo: {
siteId: 'your-cio-site-id',
apiKey: 'your-cio-api-key'
}
});

// This will send data to both Conviso and Customer.io
await client.identify('user123', {
email: '[email protected]',
firstName: 'John',
lastName: 'Doe'
});

If the Customer.io request fails, it's logged but doesn't throw an error (the Conviso request still succeeds).

Best Practices​

1. Use Consistent Identifiers​

// ✅ Good - Use consistent user IDs
await client.identify(user.id, { ... });

// ❌ Bad - Don't switch between different identifiers
await client.identify(user.email, { ... });
await client.identify(user.id, { ... });

2. Include Person Attributes Early​

// ✅ Good - Include email and name on first identify
await client.identify('user123', {
email: '[email protected]',
firstName: 'John',
lastName: 'Doe'
});

Person attributes like email, first name, and last name are crucial for many Conviso features like email campaigns, personalization, and user matching.

3. Use ISO Date Strings​

// ✅ Good - ISO 8601 format
await client.identify('user123', {
created_at: new Date().toISOString(), // "2025-10-29T10:30:00.000Z"
last_login: new Date().toISOString()
});

// ❌ Bad - Inconsistent formats
await client.identify('user123', {
created_at: '10/29/2025',
last_login: Date.now()
});

4. Avoid Sensitive Data​

// ❌ Never send sensitive data
await client.identify('user123', {
email: '[email protected]',
password: 'secret123', // DON'T DO THIS
ssn: '123-45-6789', // DON'T DO THIS
credit_card: '1234' // DON'T DO THIS
});

// ✅ Good - Only send necessary business data
await client.identify('user123', {
email: '[email protected]',
firstName: 'John',
lastName: 'Doe',
plan: 'premium'
});

Performance Considerations​

  • Concurrency: Limited by maxConcurrentRequests (default: 10)
  • Connection Pooling: HTTP connections are reused for better performance
  • Async: Always returns a Promise—use await or .then()
// Sequential (slower for multiple calls)
await client.identify('user1', { email: '[email protected]' });
await client.identify('user2', { email: '[email protected]' });

// Parallel (faster, automatically limited by SDK)
await Promise.all([
client.identify('user1', { email: '[email protected]' }),
client.identify('user2', { email: '[email protected]' })
]);