Skip to main content

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
Email & Push NOT Dual-Written

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 siteId or apiKey
  • 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
});