Dual-Write to Customer.io
Learn how to use dual-write mode to send data to both Codematic Conviso and Customer.io simultaneously.
Overview
Dual-write mode allows you to send identify() and track() calls to both Codematic Conviso and Customer.io at the same time. This is useful when:
- Migrating from Customer.io to Codematic Conviso
- Running in parallel during a transition period
- Testing Codematic Conviso before full migration
- Maintaining redundancy across platforms
Transactional emails (sendEmail) and push notifications (sendPush) are NOT sent to Customer.io to avoid duplicate messages.
Configuration
Enable dual-write by providing Customer.io credentials:
import { CDPClient } from '@codematic.io/cdp-node';
const client = new CDPClient({
cdpApiKey: process.env.CDP_API_KEY,
sendToCustomerIo: true,
customerIo: {
siteId: process.env.CUSTOMERIO_SITE_ID,
apiKey: process.env.CUSTOMERIO_API_KEY,
region: 'us' // or 'eu' for European data centers
}
});
What Gets Dual-Written
✅ Sent to Both Platforms
These operations are sent to both Conviso and Customer.io:
identify()
// Sent to both Conviso and Customer.io
await client.identify('user123', {
email: '[email protected]',
name: 'John Doe',
plan: 'premium'
});
track()
// Sent to both Conviso and Customer.io
await client.track('user123', 'purchase_completed', {
amount: 99.99,
currency: 'USD'
});
registerDevice()
// Sent to both Conviso and Customer.io
await client.registerDevice('user123', {
deviceId: 'device_abc',
platform: 'ios',
fcmToken: 'fcm_token_here'
});
❌ NOT Dual-Written
These operations are sent only to Conviso:
sendEmail()
// Only sent via Conviso (not Customer.io)
await client.sendEmail({
to: '[email protected]',
identifiers: { id: 'user123' },
transactional_message_id: 'WELCOME_EMAIL'
});
Warning displayed:
[CDP] Warning: Transactional messaging email will NOT be sent to Customer.io
to avoid sending twice. To turn this warning off set `sendToCustomerIo` to false.
sendPush()
// Only sent via Conviso (not Customer.io)
await client.sendPush({
identifiers: { id: 'user123' },
transactional_message_id: 'WELCOME_PUSH',
title: 'Welcome!',
body: 'Thanks for joining'
});
Warning displayed:
[CDP] Warning: Transactional messaging push will NOT be sent to Customer.io
to avoid sending twice. To turn this warning off set `sendToCustomerIo` to false.
Error Handling
If a Customer.io request fails, the error is logged but does not throw. The Conviso request continues normally.
// Even if Customer.io fails, Conviso request succeeds
await client.identify('user123', {
email: '[email protected]'
});
// CDP: ✅ Success
// Customer.io: ❌ Failed (logged, but doesn't throw)
This ensures your application isn't affected by Customer.io failures during the transition period.
Monitoring Dual-Write
Enable debug mode to see what's happening:
const client = new CDPClient({
cdpApiKey: process.env.CDP_API_KEY,
sendToCustomerIo: true,
customerIo: { ... },
debug: true // Enable detailed logging
});
await client.identify('user123', {
email: '[email protected]'
});
// Logs:
// [Customer.io] Identified user123
// [CDP] Identified user123
Custom Logger for Monitoring
Use a custom logger to track both platforms:
import pino from 'pino';
const logger = pino();
const client = new CDPClient({
cdpApiKey: process.env.CDP_API_KEY,
sendToCustomerIo: true,
customerIo: { ... },
cdpLogger: {
debug: (msg) => {
logger.debug(msg);
// Track dual-write metrics
if (msg.includes('[Customer.io]')) {
metrics.increment('customerio.calls');
} else if (msg.includes('[CDP]')) {
metrics.increment('cdp.calls');
}
},
error: (msg, ctx) => {
logger.error(ctx, msg);
// Alert on Customer.io failures
if (msg.includes('[Customer.io]') && msg.includes('error')) {
alerting.notify('customerio_error', ctx);
}
},
warn: (msg) => logger.warn(msg)
}
});
Troubleshooting
Customer.io Requests Failing
If Customer.io requests consistently fail:
// Check credentials
const client = new CDPClient({
cdpApiKey: process.env.CDP_API_KEY,
sendToCustomerIo: true,
customerIo: {
siteId: 'verify_this',
apiKey: 'verify_this',
region: 'us' // or 'eu'
},
debug: true // See error details
});
Common issues:
- Invalid
siteIdorapiKey - Wrong
region(US vs EU) - Customer.io rate limiting
- Network connectivity issues
Duplicate Messages
If users receive duplicate emails:
// ✅ Correct - Emails only go through CDP
await client.sendEmail({
to: '[email protected]',
identifiers: { id: 'user123' },
transactional_message_id: 'WELCOME_EMAIL'
});
// ❌ Don't do this - Will send duplicates
await customerIoClient.sendEmail({ ... }); // Manual Customer.io call
await client.sendEmail({ ... }); // Conviso call
Suppressing Warnings
To suppress dual-write warnings for emails/push:
// Option 1: Set sendToCustomerIo to false (recommended)
const client = new CDPClient({
cdpApiKey: process.env.CDP_API_KEY,
sendToCustomerIo: false // No warnings
});
// Option 2: Filter in custom logger
const client = new CDPClient({
cdpApiKey: process.env.CDP_API_KEY,
sendToCustomerIo: true,
cdpLogger: {
warn: (msg) => {
// Suppress specific warnings
if (!msg.includes('NOT be sent to Customer.io')) {
console.warn(msg);
}
}
}
});
Best Practices
1. Start with Debug Mode
const client = new CDPClient({
cdpApiKey: process.env.CDP_API_KEY,
sendToCustomerIo: true,
customerIo: { ... },
debug: true // Monitor both platforms
});
2. Monitor Both Platforms
Track metrics for both Conviso and Customer.io:
3. Plan Your Migration
- Set clear milestones
- Test thoroughly in staging
- Roll out gradually
- Have rollback plans
4. Disable Dual-Write When Done
Once migration is complete:
const client = new CDPClient({
cdpApiKey: process.env.CDP_API_KEY,
// Remove Customer.io config
debug: false
});