In-App Messages: Getting Started

In app messages let you incorporate dynamic, personalized content into your app or website with very little development or engineering and help you continue conversations across channels. By personalizing messages based on the activities your audience performs in- or outside your app, you can maintain highly relevant interactions with your audience.

What is an in-app message?

An in-app message is a message sent to a user inside your app or on your website. It’s distinct from a push notification in that a user must be in your app or on your website to receive it. They can’t get your message if they don’t open your app or login to your site.

While we refer to in-app messages as messages, you can make them persistent and use them as banners or other UI elements. They aren’t necessarily one-time messages!

How it works

When you send an in-app message, the message waits in a queue until your recipient opens your app or website on one of their devices. When a recipient opens your app or website, we poll for new messages. When a user gets a message, we log events based on their responses—whether they tap an action in the message, dismiss it, etc.

You’ll target your message to your audience by their ID or email rather than by device or browser. In-App messages go to the first device or browser that you identify a person from. After a person receives your message, they won’t get it on another device or browser session (unless they enter a campaign again or you re-broadcast your message).

sequenceDiagram Participant a as App User Participant b as SDK Participant c as Customer.io c->>c: Trigger in-app message c-->>b: If app isn't open, hold
until user opens app a->>b: User opens app b->>c: Identify User c->>b: Send in-app message b->>a: User sees
in-app message

Before you begin

Your setup path might change depending on whether you’re a developer integrating with Customer.io or a marketer who wants to send messages. Use the chart below to better understand what you, or other members of your team, must to do before you can send in-app messages.

Before you can send an in-app message, you’ll need to:

  1. Enable in-app messaging in your workspace
  2. Set up your branding and in-app messages
  3. Use your messages in campaigns or broadcasts
  4. Set up your app and/or website:
flowchart LR a{Where do
I start?} a-->|I'm a marketer|f{Are SDKs already
integrated?} f-->|yes|g[Create in-app messages] a-->|I'm a developer|b[Integrate SDK and/or
Javascript Snippet] f-.->|no, contact
a developer|b g-->c[Send messages] b-->g

Enable in-app support

  1. Go to Settings > Workspace Settings and click Get Started next to In-App.
  2. Click Enable in-app.

After you enable in-app messages, here’s how you’ll set up and use in-app messages in Customer.io.

Set up branding

Before you create messages, you’ll set your Branding rules—the colors, fonts, and padding options that you want to use across all of your messages. Branding settings help you create messages that look like a part of your app or website, and ensure consistency across your messages.

Go to the Branding tab under Content > In-App Messages to set or change your in-app branding rules.

Set up branding rules for your messages
Set up branding rules for your messages

 Branding changes take effect immediately

Changes to your branding settings affect all of your messages, even messages that are in-flight!

Set up your mobile app

If you haven’t already, you need to enable in-app support.

  1. Incorporate the in-app notifications SDK, so that your app can receive notifications from Customer.io.
  2. Identify the device user. In general, this means that your app requires people to log in, or otherwise make themselves known to you to receive an in-app message. You cannot send anonymous in-app messages.

Set up your website

After you’ve enabled the in-app message channel, you need to add our JavaScript snippet:

  1. Add one of our JavaScript snippets to your website (if you haven’t already) and add the appropriate setting to support in-app. We support in-app messaging through either our Data Pipelines or Journeys JavaScript snippets—you only need to use one of the two JavaScript options.
    • Data Pipelines JavaScript Source snippet (recommended): while Data Pipelines can be slightly more complicated to set up, it supports both your Customer.io integration and helps you send data to other downstream destinations (like Mixpanel, your data warehouse, etc).
    • Journeys JavaScript snippet: provides a simpler setup, but doesn’t support all of our Data Pipelines features.
  2. Identify people on your website—either when they login or otherwise make themselves known to you (by email or ID).

 Does your site have a CSP?

If you use a Content Security Policy (CSP) to protect your website, you’ll want to update your policy to include directives for Customer.io’s content providers. See our Content Security Policy page for more information.

Set up in-app using the Data Pipelines JavaScript Source

Data Pipelines lets you send uniform source data into Customer.io and transform it to fit the destinations where you’ll act on it. If you haven’t already set up Data Pipelines and added a JavaScript source, you’ll need to do that first.

If you’ve already added the Data Pipelines JavaScript Source to your website, you’ll need to add a line for the Customer.io In-App Plugin containing your Site IDEquivalent to the user name you’ll use to interface with the Journeys Track API; also used with our JavaScript snippets. You can find your Site ID under Settings > Workspace Settings > API Credentials, as highlighted below.

<script>
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on","addSourceMiddleware","addIntegrationMiddleware","setAnonymousId","addDestinationMiddleware"];analytics.factory=function(e){return function(){var t=Array.prototype.slice.call(arguments);t.unshift(e);analytics.push(t);return analytics}};for(var e=0;e<analytics.methods.length;e++){var key=analytics.methods[e];analytics[key]=analytics.factory(key)}analytics.load=function(key,e){var t=document.createElement("script");t.type="text/javascript";t.async=!0;t.src="https://cdp.customer.io/v1/analytics-js/snippet/" + key + "/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n);analytics._writeKey=key;analytics._loadOptions=e};analytics.SNIPPET_VERSION="4.15.3";
analytics.load(
    "YOUR_WRITE_KEY",
    { 
        "integrations": {
            "Customer.io In-App Plugin": { 
                siteId: "YOUR_JOURNEYS_SITE_ID"
            }
        }
    }
);
analytics.page();
}}();
</script>

Set up in-app using the Journeys JavaScript snippet

The Journeys JavaScript snippet is a simpler setup, but doesn’t provide all of our Data Pipelines features. If you’re just getting started with Customer.io, we recommend that you use the Data Pipelines JavaScript Source instead.

Otherwise, if you’ve already added the JavaScript snippet to your website, you’ll need to add a line to include the data-use-in-app property, highlighted in the sample below. You can find your JavaScript snippet and Site IDEquivalent to the user name you’ll use to interface with the Journeys Track API; also used with our JavaScript snippets. You can find your Site ID under Settings > Workspace Settings > API Credentials in your workspace settings.

<script type="text/javascript">
    var _cio = _cio || [];
    (function() {
        var a,b,c;a=function(f){return function(){_cio.push([f].
        concat(Array.prototype.slice.call(arguments,0)))}};b=["load","identify",
        "sidentify","track","page","on","off"];for(c=0;c<b.length;c++){_cio[b[c]]=a(b[c])};
        var t = document.createElement('script'),
            s = document.getElementsByTagName('script')[0];
        t.async = true;
        t.id    = 'cio-tracker';
        t.setAttribute('data-site-id', 'YOUR_SITE_ID');
        t.setAttribute('data-use-array-params', 'true');
        t.setAttribute('data-use-in-app', 'true');
        t.src = 'https://assets.customer.io/assets/track.js';
        //If your account is in the EU, use:
        //t.src = 'https://assets.customer.io/assets/track-eu.js'
        s.parentNode.insertBefore(t, s);
    })();
</script>

 Send page events from your single page app

If you want to take advantage of page rules from a single page application, you’ll need to send page calls to tell the SDK what “pages” people are on.

Console Errors

If you see console errors after adding your Javascript Snippet, make sure you’ve enabled in-app in your workspace settings.

Identify your website visitors

You must identify people before you can send them messages. You can do this with the identify function in either of our JavaScript snippets. The shape of the call changes slightly depending on which JavaScript snippet you use, but both snippets produce the same result for in-app messages.

//analytics.identify(id, attributes)
analytics.identify('f4ca124298', {
  email: 'person@example.com',
  first_name: 'Cool',
  last_name: 'Person',
  plan_name: 'premium'
  // Strongly recommended when you first identify someone
  created_at: 1339438758,   // This is the timestamp when the user
                            // first appears, in Unix format.
});
// Send this when a user logs in or you otherwise identify them. 

_cio.identify({
    // Required attributes — you must include your customer's identifier (this can be either an email address or a unique ID value) in a property named "id". See below for more details.
    id: 'example-person',
    
    // Strongly recommended when you first identify someone
    created_at: 1339438758,   // Timestamp in representing when the user
                              // first signed up in Unix format.

    // Other attributes (properties you'll reference with liquid)
    email: 'person@example.com',
    first_name: 'Cool',
    last_name: 'Person',
    plan_name: 'premium'
});

In the examples above, you’ll notice that both calls include an identifierThe attributes you use to add, modify, and target people. Each unique identifier value represents an individual person in your workspace.. The identifier you use in your call must match the To field in your in-app message configuration. For example, if you choose to send messages based on your audience’s email address, then your identify call must use your audience’s email address.

In-app message configuration where email is selected from the To dropdown.
In-app message configuration where email is selected from the To dropdown.

You can also send in additional attributesA 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. that are valuable to you. In the above example we send first_name, last_name and plan_name attributes. You can use these attributes to segment your audience or personalize messages.

 Include created_at when you first identify someone

The created_at attributeA 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. is useful for date-based campaign triggers, starter campaigns, and it can help you understand how long someone’s been in Customer.io. You should only pass this value with your first-ever identify calls, or you’ll overwrite your audience’s created_at value.

Listen to in-app message events

The JavaScript snippet exposes several in-app message events that you can listen to via the _cio.on and _cio.off API. All events have a payload object with a type property that indicates the type of event and detail property that contains data corresponding to the event type.

Make sure you add "on" and "off" to the list of functions you call on _cio in the snippet.

<script type="text/javascript">
    var _cio = _cio || [];
    (function() {
        var a,b,c;a=function(f){return function(){_cio.push([f].
        concat(Array.prototype.slice.call(arguments,0)))}};b=["load","identify",
        "sidentify","track","page","on","off"];for(c=0;c<b.length;c++){_cio[b[c]]=a(b[c])};
        var t = document.createElement('script'),
            s = document.getElementsByTagName('script')[0];
        t.async = true;
        t.id    = 'cio-tracker';
        t.setAttribute('data-site-id', 'YOUR_SITE_ID');
        t.setAttribute('data-use-array-params', 'true');
        t.setAttribute('data-use-in-app', 'true');
        t.src = 'https://assets.customer.io/assets/track.js';
        //If your account is in the EU, use:
        //t.src = 'https://assets.customer.io/assets/track-eu.js'
        s.parentNode.insertBefore(t, s);
    })();
</script>

Message opened event

This event is triggered when an in-app message is shown to the user. The detail property always contains the messageId property whereas the deliveryId is not present if it’s a test message.

const onMessageOpened = function (event) {
    console.log('Type: ', event.type);
    console.log('Message Id: ', event.detail.messageId);
    console.log('Delivery Id: ', event.detail.deliveryId); // not present in test messages
};

// run the listener everytime message is shown
_cio.on('in-app:message-opened', onMessageOpened);

// run the listener only once
_cio.on('in-app:message-opened', onMessageOpened, { once: true })

// turn off the listener
_cio.off('in-app:message-opened', onMessageOpened)
    • deliveryId string
      Delivery Id for the corresponding in-app message (not present in test message).
    • messageId string
      Identifier string of the in-app message.
  • type string
    Defines the event type.

    Accepted values:in-app:message-opened

Message dismissed event

This event is triggered when an in-app message is dismissed by the user.

_cio.on('in-app:message-dismissed', function (event) {
    // handle dismissed message
});
    • deliveryId string
      Delivery Id for the corresponding in-app message (not present in test message).
    • messageId string
      Identifier string of the in-app message.
  • type string
    Defines the event type.

    Accepted values:in-app:message-dismissed

Message action event

This event is triggered when the user performs an action on an in-app message.

_cio.on('in-app:message-action', function (event) {
    // handle action
    
    // optional call to dismiss the message after handling the action
    event.detail.message.dismiss();
});
    • actionName string
      The name of the action specified when building the in-app message.
    • actionValue string
      The type of action that triggered the event.
    • deliveryId string
      Delivery Id for the corresponding in-app message (not present in test message).
    • messageId string
      Identifier string of the in-app message.
  • type string
    Defines the event type.

    Accepted values:in-app:message-action

Message error event

This event is triggered when an in-app message produces an error.

_cio.on('in-app:message-error', function (event) {
    // handle error
});
    • deliveryId string
      Delivery Id for the corresponding in-app message (not present in test message).
    • messageId string
      Identifier string of the in-app message.
  • type string
    Defines the event type.

    Accepted values:in-app:message-error

Polling for messages

We normally check for new in-app messages every few seconds. But we adjust this rate when people don’t have active messages in queue to minimize unnecessary network traffic.

When you first start sending in-app messages, it may take a few minutes to ramp up in-app polling frequency to the normal rate. This won’t affect your users. But, if you have your website or app open when you send your very first test message, you’ll want to clear your cache or restart your app to see messages at the normal polling rate.

Polling FrequencyActive Messages in Queue?Description
Low (180 seconds)NoThis is the polling rate before you send your first message. We also revert to this rate if you don't send a message within 30 days *after* your last in-app message expires.
Inactive (60 seconds)NoThis is the rate for people who've received a message in the last 30 days but do not have an active message in the queue.
Active (10 seconds)YesThis is the rate for people who have an active message in the queue.
Copied to clipboard!
  Contents
Is this page helpful?