import { TrackClient, RegionUS } from "customerio-node";
import { CIO_EVENTS } from "../lib/customerio-events";
const cio = new TrackClient(
process.env.CUSTOMERIO_SITE_ID!,
process.env.CUSTOMERIO_TRACK_API_KEY!,
{ region: RegionUS }
);
interface UserProfile {
id: string;
email: string;
firstName: string;
lastName?: string;
plan: string;
companyName?: string;
}
export class MessagingService {
async onSignup(user: UserProfile, signupMethod: string): Promise<void> {
await cio.identify(user.id, {
email: user.email,
first_name: user.firstName,
last_name: user.lastName ?? "",
plan: user.plan,
company: user.companyName ?? "",
created_at: Math.floor(Date.now() / 1000),
onboarding_step: "signed_up",
});
await cio.track(user.id, {
name: CIO_EVENTS.SIGNED_UP,
data: {
method: signupMethod,
plan: user.plan,
},
});
}
async onEmailVerified(userId: string): Promise<void> {
await cio.identify(userId, {
email_verified: true,
email_verified_at: Math.floor(Date.now() / 1000),
onboarding_step: "verified",
});
await cio.track(userId, {
name: CIO_EVENTS.EMAIL_VERIFIED,
});
}
async onFeatureUsed(
userId: string,
feature: string,
metadata?: Record<string, any>
): Promise<void> {
await cio.track(userId, {
name: CIO_EVENTS.FEATURE_USED,
data: { feature, ...metadata },
});
await cio.identify(userId, {
last_active_at: Math.floor(Date.now() / 1000),
});
}
async onUpgrade(userId: string, from: string, to: string, mrr: number): Promise<void> {
await cio.identify(userId, {
plan: to,
mrr,
upgraded_at: Math.floor(Date.now() / 1000),
});
await cio.track(userId, {
name: CIO_EVENTS.UPGRADE_COMPLETED,
data: { from_plan: from, to_plan: to, mrr },
});
}
async onCancellation(userId: string, reason: string): Promise<void> {
await cio.identify(userId, {
plan: "cancelled",
cancelled_at: Math.floor(Date.now() / 1000),
cancellation_reason: reason,
});
await cio.track(userId, {
name: CIO_EVENTS.SUBSCRIPTION_CANCELLED,
data: { reason },
});
}
}
import { MessagingService } from "../services/customerio-messaging";
const messaging = new MessagingService();
router.post("/signup", async (req, res) => {
const user = await db.createUser(req.body);
messaging.onSignup(
{
id: user.id,
email: user.email,
firstName: user.firstName,
plan: user.plan,
},
req.body.signupMethod
).catch((err) => console.error("CIO signup tracking failed:", err));
res.json({ user });
});
router.post("/verify-email", async (req, res) => {
await db.verifyEmail(req.user.id);
messaging.onEmailVerified(req.user.id).catch(console.error);
res.json({ verified: true });
});