Node.js Source

How it works

Our Node.js library helps you record source events from your node-side code. Requests from your Node.js server go to our servers, and we route your data to your destinations.

This library uses an internal queue so that your identify and track calls are fast and non-blocking. It also batches requests and flushes asynchronously to Customer.io’s servers.

Like our other libraries, you can log anonymous activity—track and page events—with an anonymousId. When you identify a person, you can pass the anonymousId and we’ll associate the anonymous activity with the identified person.

Getting Started

 We support node 14 or later

If you’re on an earlier version of node, you should upgrade to take advantage of our Node.js library.

  1. Go to the tab and click Sources.

  2. Click Add Source and pick Node.js.

  3. Give the source a Name and click Complete Setup. The name is simply a friendly name to help you find and recognize your source in Customer.io.

  4. On your Node server, install the source:

     # npm
     npm install @customerio/cdp-analytics-node
     # yarn
     yarn add @customerio/cdp-analytics-node
     # pnpm
     pnpm install @customerio/cdp-analytics-node
    
  5. Use the Analytics constructor and initialize Customer.io with your API Key. If you’re in our EU region, make sure you set the host parameter to https://cdp-eu.customer.io.

     import { Analytics } from '@customerio/cdp-analytics-node'
     // or, if you use require:
     const { Analytics } = require('@customerio/cdp-analytics-node')
    
     // instantiation
     const analytics = new Analytics({ 
       writeKey: '<YOUR_API_KEY>' 
       // if you're in our EU region
       // host: 'https://cdp-eu.customer.io',
     })
    

    This creates an instance of Analytics that you can use to send data to Customer.io. The default initialization settings are production-ready and queue 20 messages before sending requests.

Now you’re ready to send requests to Customer.io. Check out our API reference, or read further to see example requests and understand the types of requests you can make using our Node.js library.

As you work on your integration, you might want to use development settings.

If you’re in our EU data center

You’ll need to set the endpoint parameter to set our EU URL (https://cdp-eu.customer.io). Note that our EU regional endpoints account for the location of your data in Customer.io; they don’t account for the locations of your sources and destinations.

import { Analytics } from '@customerio/cdp-analytics-node'

const analytics = new Analytics({ 
  writeKey: '<YOUR_API_KEY>' 
  host: 'https://cdp-eu.customer.io',
})

Identify

The identify method tells Data Pipelines who the current website visitor is, and lets you assign unique traitsA key-value pair that you associate with a person or an object—like a person’s name, the date they were created in your workspace, or a company’s billing date etc. Use attributes to target people and personalize messages. Attributes are analogous to traits in Data Pipelines. to a person.

You should call identify when a user creates an account, logs in, etc. You can also call it again whenever a person’s traits change. We’ve shown a typical call with a traits object, but we’ve listed all the fields available in an identify call below.

You can send an identify call with an anonymousId and/or userId.

  • anonymousId only: This assign traits to a person before you know who they are.
  • userId only: Identifies a user and sets traits.
  • both userId and anonymousId: Associates the data sent in previous anonymous page, track, and identify calls with the person you identify by userId.
analytics.identify({
  userId: '019mr8mf4r',
  traits: {
    name: 'Cool Person',
    email: 'cool.person@example.com',
    plan: 'Enterprise',
    friends: 42
  }
});

In most cases, you’ll simply provide userId, anonymousId, and traits, but we’ve provided the full list of writable-fields below. You’ll find the complete source payload in our API documentation.

  • anonymousId string
    A unique substitute for a User ID in cases when you don’t have an absolutely unique identifier. Our libraries generate this value automatically to help you track people before they sign up, log in, provide their email, etc.
    • active boolean

      Whether a user is active.

      This is usually used to flag an .identify() call to just update the traits but not “last seen.”

    • channel string
      The channel the event originated from.

      Accepted values:browser,server,mobile

    • ip string
      The user’s IP address. This isn’t captured by our libraries, but by our servers when we receive client-side events (like from our JavaScript source).
    • locale string
      The local string for the current user, e.g. en-US.
    • userAgent string
      The user agent of the device making the request
      • content string
      • medium string
        The type of traffic a person/event originates from, like email, or referral.
      • name string
        The campaign name.
      • source string
        The source of traffic—like the name of your email list, Facebook, Google, etc.
      • term string
        The keyword term(s) a user came from.
      • Additional UTM Parameters* string
      • keywords array of [ strings ]
        A list/array of keywords describing the page’s content. The keywords are likely the same as, or similar to, the keywords you would find in an HTML meta tag for SEO purposes. This property is mainly used by content publishers that rely heavily on pageview tracking. This isn’t automatically collected.
      • name string
        The name of the page. Reserved for future use.
      • path string
        The path portion of the page’s URL. Equivalent to the canonical path which defaults to location.pathname from the DOM API.
      • referrer string
        The previous page’s full URL. Equivalent to document.referrer from the DOM API.
      • search string
        The query string portion of the page’s URL. Equivalent to location.search from the DOM API.
      • title string
        The page’s title. Equivalent to document.title from the DOM API.
      • url string
        A page’s full URL. Segment first looks for the canonical URL. If the canonical URL is not provided, Segment uses location.href from the DOM API.
    • Enabled/Disabled integrations* boolean
  • messageId string
    A unique identifier for a Data Pipelines event, ensuring that each individual event is unique.
  • receivedAt string  (date-time)
    The ISO-8601 timestamp when Data Pipelines receives an event.
  • sentAt string  (date-time)
    The ISO-8601 timestamp when a library sends an event to Data Pipelines.
  • timestamp string  (date-time)
    The ISO-8601 timestamp when the event originally took place. This is mostly useful when you backfill data past events. If you’re not backfilling data, you can leave this field empty and we’ll use the current time or server time.
    • createdAt string  (date-time)
      We recommend that you pass date-time values as ISO 8601 date-time strings. We convert this value to fit destinations where appropriate.
    • email string
      A person’s email address. In some cases, you can pass an empty userId and we’ll use this value to identify a person.
    • Additional Traits* any type
      Traits that you want to set on a person. These can take any JSON shape.
  • type string
    The event type. This is set automatically by the request method/endpoint.

    Accepted values:identify

  • userId string
    The unique identifier for a person. This value should be unique across systems, so you recognize the same person in your sources and destinations.
  • version number
    The version of the API that received the event, automatically set by Customer.io.

Track

The track method tells us about actions people take—the events people perform—on your site. Every track call represents an event. When you send an event with a track call, you can provide associated properties about the event. For more information, see the track method reference.

You should track events that are indicators on your site, or the kinds of things that you want to know that your audience does on your website, like Video Viewed, Item Purchased or Article Bookmarked. In some products—like Customer.io Journeys—you can use events to trigger messages and other downstream actions.

You can send events with an anonymousId or a userId. Calls that you make with an anonymousId are associated with a userId when you identify someone by their userId.

Track calls typically contain an event name—so you know what a user did—and a series of properties, which contain additional information about the event. The examples below show added_to_cart events, containing properties that let you know about the product a person added to their shopping cart.

analytics.track({
  userId: '019mr8mf4r',
  event: 'added_to_cart',
  properties: {
    product: "shoes",
    revenue: 39.95,
    qty: 1
    size: 9
  }
});
analytics.track({
  anonymousId: '48d213bb-95c3-4f8d-af97-86b2b404dcfe',
  event: 'added_to_cart',
  properties: {
    product: "shoes",
    revenue: 39.95,
    qty: 1
    size: 9
  }
});

In most cases, you’ll simply provide an ID for the user, the event, and properties, but we’ve provided the full list of writable-fields below. You’ll find the complete source payload in our API documentation.

    • active boolean

      Whether a user is active.

      This is usually used to flag an .identify() call to just update the traits but not “last seen.”

    • channel string
      The channel the event originated from.

      Accepted values:browser,server,mobile

    • ip string
      The user’s IP address. This isn’t captured by our libraries, but by our servers when we receive client-side events (like from our JavaScript source).
    • locale string
      The local string for the current user, e.g. en-US.
    • userAgent string
      The user agent of the device making the request
      • content string
      • medium string
        The type of traffic a person/event originates from, like email, or referral.
      • name string
        The campaign name.
      • source string
        The source of traffic—like the name of your email list, Facebook, Google, etc.
      • term string
        The keyword term(s) a user came from.
      • Additional UTM Parameters* string
      • keywords array of [ strings ]
        A list/array of keywords describing the page’s content. The keywords are likely the same as, or similar to, the keywords you would find in an HTML meta tag for SEO purposes. This property is mainly used by content publishers that rely heavily on pageview tracking. This isn’t automatically collected.
      • name string
        The name of the page. Reserved for future use.
      • path string
        The path portion of the page’s URL. Equivalent to the canonical path which defaults to location.pathname from the DOM API.
      • referrer string
        The previous page’s full URL. Equivalent to document.referrer from the DOM API.
      • search string
        The query string portion of the page’s URL. Equivalent to location.search from the DOM API.
      • title string
        The page’s title. Equivalent to document.title from the DOM API.
      • url string
        A page’s full URL. Segment first looks for the canonical URL. If the canonical URL is not provided, Segment uses location.href from the DOM API.
  • event string
    Required The name of the event
    • Enabled/Disabled integrations* boolean
  • messageId string
    A unique identifier for a Data Pipelines event, ensuring that each individual event is unique.
    • Event Properties* any type
      Additional properties that you want to capture in the event. These can take any JSON shape.
  • receivedAt string  (date-time)
    The ISO-8601 timestamp when Data Pipelines receives an event.
  • sentAt string  (date-time)
    The ISO-8601 timestamp when a library sends an event to Data Pipelines.
  • timestamp string  (date-time)
    The ISO-8601 timestamp when the event originally took place. This is mostly useful when you backfill data past events. If you’re not backfilling data, you can leave this field empty and we’ll use the current time or server time.
  • type string
    Required The event type. This is set automatically by the request method/endpoint.

    Accepted values:track

  • userId string
    Required The unique identifier for a person. This value should be unique across systems, so you recognize the same person in your sources and destinations.
  • version number
    The version of the API that received the event, automatically set by Customer.io.

Page

The Page method records page views on your website, along with optional extra information about the page a person visited.

If you’re using Customer.io’s client-side set up in combination with the Node.js library, page calls are already tracked for you by default on any page that loads the client-side script.

But, if you have a single page app or you don’t use our JavaScript client library on your website, you’ll need to send your own page calls.

analytics.page({
  userId: '019mr8mf4r',
  category: 'Docs',
  name: 'Customer.io CDP',
  properties: {
    url: 'https://customer.io/docs/cdp/',
    path: '/docs/cdp/',
    title: 'Customer.io CDP',
    referrer: 'https://customer.io'
  }
});

In most cases, you’ll simply provide an ID for the user and the page name, but we’ve provided the full list of writable-fields below. You’ll find the complete source payload in our API documentation.

    • active boolean

      Whether a user is active.

      This is usually used to flag an .identify() call to just update the traits but not “last seen.”

    • channel string
      The channel the event originated from.

      Accepted values:browser,server,mobile

    • ip string
      The user’s IP address. This isn’t captured by our libraries, but by our servers when we receive client-side events (like from our JavaScript source).
    • locale string
      The local string for the current user, e.g. en-US.
    • userAgent string
      The user agent of the device making the request
      • content string
      • medium string
        The type of traffic a person/event originates from, like email, or referral.
      • name string
        The campaign name.
      • source string
        The source of traffic—like the name of your email list, Facebook, Google, etc.
      • term string
        The keyword term(s) a user came from.
      • Additional UTM Parameters* string
      • keywords array of [ strings ]
        A list/array of keywords describing the page’s content. The keywords are likely the same as, or similar to, the keywords you would find in an HTML meta tag for SEO purposes. This property is mainly used by content publishers that rely heavily on pageview tracking. This isn’t automatically collected.
      • name string
        The name of the page. Reserved for future use.
      • path string
        The path portion of the page’s URL. Equivalent to the canonical path which defaults to location.pathname from the DOM API.
      • referrer string
        The previous page’s full URL. Equivalent to document.referrer from the DOM API.
      • search string
        The query string portion of the page’s URL. Equivalent to location.search from the DOM API.
      • title string
        The page’s title. Equivalent to document.title from the DOM API.
      • url string
        A page’s full URL. Segment first looks for the canonical URL. If the canonical URL is not provided, Segment uses location.href from the DOM API.
    • Enabled/Disabled integrations* boolean
  • messageId string
    A unique identifier for a Data Pipelines event, ensuring that each individual event is unique.
  • name string
    Required The name of the page.
    • category string
      The category of the page. This might be useful if you have a single page routes or have a flattened URL structure.
    • Page Properties* any type
      Additional properties tha tyou want to send with the page event. By default, we capture `url`, `title`, and stuff.
  • receivedAt string  (date-time)
    The ISO-8601 timestamp when Data Pipelines receives an event.
  • sentAt string  (date-time)
    The ISO-8601 timestamp when a library sends an event to Data Pipelines.
  • timestamp string  (date-time)
    The ISO-8601 timestamp when the event originally took place. This is mostly useful when you backfill data past events. If you’re not backfilling data, you can leave this field empty and we’ll use the current time or server time.
  • type string
    Required The event type. This is set automatically by the request method/endpoint.

    Accepted values:page

  • userId string
    Required The unique identifier for a person. This value should be unique across systems, so you recognize the same person in your sources and destinations.
  • version number
    The version of the API that received the event, automatically set by Customer.io.

Group

The Group method associates an identified person with a group—like a company, organization, project, online class or any other collective noun you come up with for the same concept. In Customer.io Journeys, we call groups objectsNot to be confused with a JSON object, an object in Customer.io is a non-person entity that you can associate with one or more people—like a company, account, or online course. You can use objects to message people based on changes to their company, account, or course itinerary..

Group calls are useful for destinations where you maintain relationships between people and larger organizations, like in Customer.io! In Customer.io Journeys, you can store groups as objectsNot to be confused with a JSON object, an object in Customer.io is a non-person entity that you can associate with one or more people—like a company, account, or online course. You can use objects to message people based on changes to their company, account, or course itinerary., and trigger campaigns based on a person’s relationship to an object—like an account, online class, and so on.

Find more details about group, including the group payload, in our API spec.

analytics.group({
  userId: '019mr8mf4r',
  groupId: '56',
  traits: {
    name: 'Initech',
    description: 'Accounting Software'
  }
});

In most cases, you’ll simply provide an ID for the user, the groupId, and traits, but we’ve provided the full list of writable-fields below. You’ll find the complete source payload in our API documentation.

 Include objectTypeId if Customer.io Journeys is a destination

Customer.io Journeys lets you set up groups (called objectsNot to be confused with a JSON object, an object in Customer.io is a non-person entity that you can associate with one or more people—like a company, account, or online course. You can use objects to message people based on changes to their company, account, or course itinerary.) of different types; the object type is an incrementing integer beginning at 1. If you use Customer.io Journeys as a destination, you should include the object type ID or we’ll assume that the object type is 1.

    • active boolean

      Whether a user is active.

      This is usually used to flag an .identify() call to just update the traits but not “last seen.”

    • channel string
      The channel the event originated from.

      Accepted values:browser,server,mobile

    • ip string
      The user’s IP address. This isn’t captured by our libraries, but by our servers when we receive client-side events (like from our JavaScript source).
    • locale string
      The local string for the current user, e.g. en-US.
    • userAgent string
      The user agent of the device making the request
      • content string
      • medium string
        The type of traffic a person/event originates from, like email, or referral.
      • name string
        The campaign name.
      • source string
        The source of traffic—like the name of your email list, Facebook, Google, etc.
      • term string
        The keyword term(s) a user came from.
      • Additional UTM Parameters* string
      • keywords array of [ strings ]
        A list/array of keywords describing the page’s content. The keywords are likely the same as, or similar to, the keywords you would find in an HTML meta tag for SEO purposes. This property is mainly used by content publishers that rely heavily on pageview tracking. This isn’t automatically collected.
      • name string
        The name of the page. Reserved for future use.
      • path string
        The path portion of the page’s URL. Equivalent to the canonical path which defaults to location.pathname from the DOM API.
      • referrer string
        The previous page’s full URL. Equivalent to document.referrer from the DOM API.
      • search string
        The query string portion of the page’s URL. Equivalent to location.search from the DOM API.
      • title string
        The page’s title. Equivalent to document.title from the DOM API.
      • url string
        A page’s full URL. Segment first looks for the canonical URL. If the canonical URL is not provided, Segment uses location.href from the DOM API.
  • groupId string
    Required ID of the group
    • Enabled/Disabled integrations* boolean
  • messageId string
    A unique identifier for a Data Pipelines event, ensuring that each individual event is unique.
  • objectTypeId string

    If you use Customer.io Journeys as a destination, this value is the type of group/object your group belongs to; object type IDs are stringified integers. If you don’t include this value, we assume the object type ID is 1. See objects in Customer.io Journeys for more information.

    You can include this value as objectTypeId at the top level of your payload or as object_type_id in the traits object.

  • receivedAt string  (date-time)
    The ISO-8601 timestamp when Data Pipelines receives an event.
  • sentAt string  (date-time)
    The ISO-8601 timestamp when a library sends an event to Data Pipelines.
  • timestamp string  (date-time)
    The ISO-8601 timestamp when the event originally took place. This is mostly useful when you backfill data past events. If you’re not backfilling data, you can leave this field empty and we’ll use the current time or server time.
    • object_type_id string

      If you use Customer.io Journeys as a destination, this value is the type of group/object your group belongs to; object type IDs are stringified integers. If you don’t include this value, we assume the object type ID is 1. See objects in Customer.io Journeys for more information.

      You can include this value as objectTypeId at the top level of your payload or as object_type_id in the traits object.

    • Group Traits* any type
      Additional traits you want to associate with this group.
  • type string
    Required The event type. This is set automatically by the request method/endpoint.

    Accepted values:group

  • userId string
    Required The unique identifier for a person. This value should be unique across systems, so you recognize the same person in your sources and destinations.
  • version number
    The version of the API that received the event, automatically set by Customer.io.

Alias

The Alias method combines two previously unassociated user identities. Some destinations automatically reconcile profiles with different identifiers based on whether you send anonymousId, userId, or another trait that the destination expects to be unique. But for destinations that don’t, you may need to send alias requests to do this.

In general, you won’t need to use the alias call; we try to handle user identification gracefully, so that you don’t need to merge profiles. But you may need to send alias calls to manage user identities in some destinations.

For example, in Mixpanel it’s used to associate an anonymous user with an identified user once they sign up.

Here’s how you might use the alias call. In this case, we start with an anonymous_user and switch to an email address when a person provides their userId.

// the anonymous user does actions ...
analytics.track({ userId: 'anonymous_user', event: 'Anonymous Event' })
// the anonymous user signs up and is aliased
analytics.alias({ previousId: 'anonymous_user', userId: 'identified@example.com' })
// the identified user is identified
analytics.identify({ userId: 'identified@example.com', traits: { plan: 'Free' } })
// the identified user does actions ...
analytics.track({ userId: 'identified@example.com', event: 'Identified Action' })
  • previousId string
    Required The userId that you want to merge into the canonical profile.
  • userId string
    Required The userId that you want to keep. This is required if you haven’t already identified someone with one of our web or server-side libraries.

Configuration

The first argument for the Analytics constructor is your API key. The second an optional dictionary of configuration settings.

var analytics = new Analytics('YOUR_API_KEY', {
  flushAt: 20,
  flushInterval: 10000,
  enable: false
});
SettingDetails
flushAt(Number) The number of messages to enqueue before flushing.
flushInterval(Number) The number of milliseconds to wait before flushing the queue automatically.
enable(Boolean) Enable (default) or disable flush. Useful when writing tests and you do not want to send data to Customer.io Servers.

Error Handling

You can use the optional errorHandler property available to the class constructor’s options. Our node library uses Axios, so error messages are thrown by Axios.

If you specify the error handler, we’ll call errorHandler(axiosError) when you have an error instead of re-throwing the Axios error. If this fails when flushed, Analytics won’t throw an exception. Instead, we’ll log Axios error to the console.

const Analytics = require('cdp-analytics-node');

const client = new Analytics('api key', {
  errorHandler: (err) => {
    console.error('cdp-analytics-node flush failed.')
    console.error(err)
  }
});

client.track({
  event: 'event name',
  userId: 'user id'
});

Development

While implementing Data Pipelines, you might want to make our library flush after every event or call. This can help you test your implementation and make sure that all of your calls work properly before you start making calls from your production environment.

var analytics = new Analytics('YOUR_API_KEY', { flushAt: 1 });

Selecting Destinations

You can pass an integrations object to outgoing calls to turn certain destinations on or off. By default all destinations are enabled. Passing false for an integration disables the call to that destination.

You might want to do this for things like alias calls, which aren’t supported by all destinations. All: false disables all destinations except the ones you explicitly specify.

analytics.track({
  event: 'Membership Upgraded',
  userId: '97234974',
  integrations: {
    'All': false,
    'Mixpanel': true,
    'Google Analytics': false
  }
})

Destination flags are case sensitive. You’ll find each integration’s name at the top of each integration’s page in the docs.

 You can filter track calls on the source’s Schema tab

We recommend that you filter events in our UI if you can. It’s easier than writing code, and you can update your source or make changes to your filters without involving developers!

Backfilling historical data

You can backfill data by adding a timestamp to your calls. This can be helpful if you’ve just switched to Customer.io or you’re getting started with Data Pipelines and want to send historical data.

You can only do this for destinations that accept timestamped data (most analytics tools like Mixpanel and Amplitude do). The notable destination that doesn’t support timestamped data is Google Analytics.

 Leave out the timestamp if you’re tracking real-time events

If you’re only tracking things as they happen, you can leave the timestamp out of your calls and we’ll timestamp requests for you.

Batching

Our libraries are built to support high performance environments. It’s safe to use this library on a web server that serves hundreds of requests per second.

But every method you invoke does not result in an HTTP request. Instead, we queue requests in memory and then flush them in batches, which allows for more efficient operation.

By default, our Node.js source library flushes:

  • The very first call.
  • Every 20 messages (controlled by options.flushAt).
  • If 10 seconds pass after the previous flush (controlled by options.flushInterval)

There is a maximum of 500KB per batch request and 32KB per call. If you don’t want to batch messages, you can turn batching off by setting the flushAt option to 1.

Batching means that your message might not get sent right away. Every method call takes an optional callback, which you can use to know when a particular message is flushed from the queue.

analytics.track({
  userId: '019mr8mf4r',
  event: 'Ultimate Played'
}, function(err, batch){
  if (err) // There was an error flushing your message...
  // Your message was successfully flushed!
});

You can also invoke the closeAndFlush method to stop processing data and ensure a clean exit for your program. This is especially useful for serverless applications like AWS Lambda, Cloudflare Workers when you need to ensure that your data is delivered before suspending the lambda.

await analytics.closeAndFlush();

console.log('Flushed, and now this program can exit!');

Flush long running processes

Because we queue messages, you’ll want to capture interruptions (for example, a server restart) and call closeAndFlush so that you don’t inadvertently drop requests when you need to perform maintenance on your server.

import { randomUUID } from 'crypto';
import Analytics from 'cdp-analytics-node'

const API_KEY = '...';

const analytics = new Analytics(API_KEY, { flushAt: 10 });

analytics.track({
  anonymousId: randomUUID(),
  event: 'Test event',
  properties: {
    name: 'Test event',
    timestamp: new Date()
  }
});

const exitGracefully = async (code) => {
  console.log('Flushing events');
  await analytics.closeAndFlush(function(err, batch) {
    console.log('Flushed, and now this program can exit!');
    process.exit(code);
  });
};

[
  'beforeExit', 'uncaughtException', 'unhandledRejection',
  'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP',
  'SIGABRT','SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV',
  'SIGUSR2', 'SIGTERM',
].forEach(evt => process.on(evt, exitGracefully));

function logEvery2Seconds(i) {
    setTimeout(() => {
        console.log('Infinite Loop Test n:', i);
        logEvery2Seconds(++i);
    }, 2000);
}

logEvery2Seconds(0);

Multiple Clients

Different parts of your application may require different types of batching, or even sending to multiple Customer.io sources. In these cases, you can initialize multiple instances of Analytics with different settings!

var Analytics = require('cdp-analytics-node');
var marketingAnalytics = new Analytics('MARKETING_API_KEY');
var appAnalytics = new Analytics('APP_API_KEY');
Copied to clipboard!
  Contents
Is this page helpful?