Your Customer.io In-App Message Isn't Showing. Here's the Five-Minute Decision Tree

The campaign is live. The workflow shows the message as sent. And on the screen where it's supposed to appear... nothing. No bounce, no error, no warning in the campaign view. In-app is the one Customer.io channel that fails in complete silence.

The good news: the causes are a short, knowable list, and you can check most of them without touching the codebase. This post is that list. Six checks in order, the one console test that settles whether the problem is yours or your developer's, and a clear line for when to stop and escalate. Five minutes, start to finish.

Why in-app messages fail silently

"Sent" doesn't mean seen. When Customer.io sends an in-app message, it queues it against the person. The message only displays later, on their device, when three conditions line up: they visit a page or screen matching your page rules, they're identified, and the message hasn't expired (Send in-app messages docs). Email gives you a bounce when something breaks. In-app gives you nothing, because from Customer.io's side nothing broke. The message is sitting in the queue, waiting for conditions that never arrive.

That makes the campaign view almost useless for diagnosis. The sent metric logs when the message queues, not when a human sees it. A message can sit "sent" for thirty days and get quietly cancelled without anyone ever knowing it existed.

Failing without errors is something of a Customer.io habit—Liquid templates render blank rather than erroring when a property goes null—but in-app takes it further. The whole channel is silent by design, so diagnosis means walking the delivery conditions in order.

The decision tree, in order

Work down the list and stop at the first "no". Each check takes under a minute, and most failures live in the first three.

1. Is the SDK installed and initialised?

The in-app editor works before any SDK exists, which is exactly how teams get caught. You can build a message, drop it into a workflow and launch it, but your audience won't see anything until you've integrated the SDK for your app or website (Send in-app messages docs). On web, that means the JavaScript snippet is installed and identifying visitors. On mobile, the in-app messaging package must be initialised alongside the main SDK—on iOS that's CioMessagingInApp initialised after CustomerIO.initialize (Set up in-app messaging, iOS SDK docs).

You can check this without a developer. Go to Workspace Settings > In-App and look at the setup steps: is in-app enabled, and does the "connect your app" step show as complete? Then click Send Test on that same settings page (Get started: web and in-app messages). If the settings-page test won't display, no campaign message will ever display, and you've found your answer at step one.

2. Is the person identified?

In-app messages target people, not devices. You aim a message at an ID or email address, and the SDK only delivers it when someone identified as that person opens the app or site (Get started docs). If your visitor never logged in, or the identify call never fired for them, the message has nobody to attach to. It queues, waits, and expires.

This is the single most common cause of "works for me, invisible to users". When you test, you're identified—you sent the test to yourself and you're logged in. Your real audience includes people browsing logged out, people on a marketing page before the snippet identifies them, and people whose identify call silently failed.

One exception, so you don't chase a ghost: anonymous in-app messages, a Premium and Enterprise feature, deliberately target people you haven't identified and exclude everyone you have (Anonymous messages docs). They're created from their own Anonymous section, not a campaign workflow, and on web they need the anonymousInApp: true flag in your JavaScript configuration. For a standard workflow message, identification is a hard requirement.

3. Has the app or site been opened in the delivery window?

A person only gets an in-app message by opening your app or website while the message is live. There's no push involved... the message waits in a queue until a device shows up to collect it (Get started docs).

Two details here cause most of the confusion. First, the message appears in the first device or browser the person opens after you send it, and after that they won't get it on another device or browser session (Get started docs). If your test user glanced at their phone before you checked the laptop, the message came and went.

Second, polling. The SDK checks for new messages every 10 seconds when a person has an active message in queue, every 60 seconds when they've received one in the last 30 days but have none queued, and every 180 seconds before your workspace has ever sent one—or after 30 days of in-app inactivity (Get started docs). That last rate is the first-test trap: send your first-ever message with the browser or app already open and it can take up to three minutes to appear. Close the app or browser before your first test, or clear the cache and refresh to kick the polling rate up (Test your in-app messages docs).

4. Has the message expired?

By default, in-app messages expire 30 days after they're sent. If the person doesn't see the message within that window, Customer.io cancels it (Send in-app messages docs). You can set expiry to a fixed date, a relative period after send, or a Liquid-driven value, and the maximum window is 60 days.

Check the message's Expiration setting against when the campaign fired for this person. A coupon message set to expire after three days, queued on a Friday for a user who logs in fortnightly, will be cancelled before they ever return. The fix is either a longer window or an honest acceptance that in-app is the wrong channel for that audience's visit frequency.

5. Do the page or screen targeting rules match?

Page rules are where most messages quietly die, because four separate traps live here.

Case sensitivity first. Page rules are case sensitive: web rules should always be lowercase, and mobile rules must match the casing of the name in your screen events exactly (Send in-app messages docs). A rule for /Dashboard on web matches nothing.

Second, "page" means different things per platform. On your website it's the URL, unless you pass page calls with a custom name. In your mobile app it's the name value from screen events—and that means screen tracking has to be switched on in the SDK at all. If Customer.io doesn't recognise the screen name in your rule, your audience never sees the message (Set up in-app messaging, iOS SDK docs). Screen names drifting between platforms—DashboardActivity on Android, DashboardViewController on iOS, /dashboard on web—is exactly the kind of naming problem a written event schema prevents.

Third, the include/exclude logic. A person must match at least one include rule and no exclude rules. One matching exclude vetoes everything, even if an include also matches (Send in-app messages docs).

Fourth, single-page applications. Customer.io only sends page calls automatically when a page loads, and in an SPA that happens once. If your site is an SPA and nobody wired up manual page calls on route changes, page-rule-targeted messages will never fire after the first screen (Test your in-app messages docs).

The quickest check for all four: the Test Page Rules box below the rule editor. Paste the real URL or screen name a user would be on and see whether the rule matches (Send in-app messages docs). One extra wrinkle for tooltips: the CSS target element must exist on the page, or the tooltip never appears no matter what the rules say.

6. Is the person eligible in the workflow?

If everything device-side checks out, climb back up to the workflow. Open the person's profile in Customer.io and look at their journey: did they reach the in-app step at all?

Filters and segment conditions ahead of the message remove people without announcing it—segment conditions built for precision also exclude silently. A branch that keys on email opens hasn't been trustworthy since Apple's Mail Privacy Protection started pre-fetching every tracking pixel, so a person may have taken a different path than you assumed. And if your in-app message sits in one of several workflows linked by Send Event, confirm the upstream event fired before blaming the message.

Two eligibility causes are specific to in-app. Priority: Customer.io displays one modal at a time, and a queued higher-priority message won't replace one that's already showing—so another message may be occupying the slot (Send in-app messages docs). And app versions: people on an app build whose SDK predates in-app support will show messages as sent that can never be opened. Customer.io recommends segmenting on platform and cio_sdk_version to keep those people out of in-app sends (Send in-app messages docs).

The one console test that proves delivery

For web messages, there's a single test that cuts the problem in half. Open your site, open the browser console, and run a manual identify call:

cioanalytics.identify("<id or email>")

...using the ID or email of the person you sent a test to. If the message appears after the manual call, your integration delivers messages fine, and the problem is how your production code sends identify calls (Test your in-app messages docs). That's the whole "worked in test, silent in production" mystery in one line: Send Test plus your own logged-in session works because you were identified. Production fails because your users aren't.

While the console is open, check the network tab. You should see requests for i (identify) and p (page) before a message arrives (Test your in-app messages docs). No i calls means an identification problem. No p calls on route changes means a single-page-app problem. Either way, you now know which sentence to say to your developer.

When to stop and call a developer

The split is clean. Expiry settings, page rules, workflow eligibility and audience conditions are yours—fix them in the Customer.io UI. SDK installation, initialisation order, identify calls, screen tracking and SDK upgrades are code, and that's developer territory.

Make the handover useful. Give them the result of the console test, then point them at debug logging. Setting the SDK's log level to debug lets you trace every aspect of an in-app message, from receipt to dismissal (Test your in-app messages docs), and a debug log replicating the issue is the first thing Customer.io support asks for (Troubleshooting mobile issues docs). Have them check the installed SDK version against the latest—as of June 2026 that's iOS 4.5.0, Android 4.18.0, React Native 6.5.0, Flutter 4.1.0 and Expo 3.6.0—because minor version updates typically carry the bug fixes. Customer.io also publishes sample apps for each SDK to diff against, and its MCP server includes an integration tool that troubleshoots implementations directly from an AI coding assistant (Troubleshooting mobile issues docs).

Then, once the message finally shows, make it worth showing. A welcome modal nobody asked for is a waste of a fixed channel... there are three behaviour-triggered in-app messages worth building first.

Frequently asked questions

Why is my Customer.io in-app message not showing?

One of six conditions is failing: no SDK installed or initialised, person not identified, app or site not opened since the send, message expired, page or screen rules not matching, or workflow eligibility (Send in-app messages docs). Check them in that order—most failures sit in the first three.

How do I test whether an in-app message will deliver?

Use Send Test from the campaign or the in-app settings page, then open your app or site as the test recipient (Test your in-app messages docs). On web, you can also run cioanalytics.identify("<id or email>") in the browser console; if the message appears after the manual call, delivery works and the problem is in your site's identify calls.

Does the app need to be open for an in-app message to appear?

Yes. An in-app message waits in a queue until the person opens your app or website—it can't reach anyone who doesn't come back (Get started: web and in-app messages docs). That's the defining difference from push notifications, which arrive whether the app is open or not.

What does the in-app message expiry setting do?

It limits how long a queued message stays available. Messages expire 30 days after sending by default, and if the person doesn't see the message within the window, Customer.io cancels it (Send in-app messages docs). You can set a fixed date, a relative period, or a Liquid-driven value, up to a maximum of 60 days.

Why did my in-app message work in testing but not in production?

Because your test session was identified and your production users aren't. Send Test works when you're logged in as the recipient, but real visitors who haven't logged in—or whose identify call fails—never collect the message (Test your in-app messages docs). Single-page apps add a second cause: page calls don't fire on route changes unless your developers send them manually.

Do I need to identify a person for in-app messages to work?

For standard campaign and broadcast messages, yes—delivery requires the person to be identified on the device (Send in-app messages docs). The exception is anonymous in-app messages, a Premium and Enterprise feature that targets only unidentified visitors and excludes everyone you've identified (Anonymous messages docs).

Why does my first in-app test take up to three minutes to appear?

Before your workspace sends its first message, SDKs poll for messages every 180 seconds to minimise network traffic (Get started docs). The rate only updates if your app or browser is closed when the first message goes out. Close the app before your first test, or clear the cache and refresh after sending.

Why do my page rules match on web but not on mobile?

Mobile page rules match against the name in your screen events, are case sensitive, and require screen tracking to be enabled in the SDK (Send in-app messages docs). Web rules match URLs and should be lowercase. A rule that works for /dashboard on web needs a separate rule matching Dashboard (or whatever your screen event sends) for the app.

Can an in-app message appear on more than one device?

No. The message displays in the first device or browser the person opens after you send it, and they won't get it again on another device or session unless they re-enter the campaign or you re-broadcast it (Get started docs). If a test seems to have vanished, check whether it already displayed somewhere else.

Why does my tooltip message never appear?

Tooltips anchor to a CSS selector, and if the target element doesn't exist on the page, the tooltip won't display at all (Send in-app messages docs). Make sure the element is present on every page your page rules cover, and prefer stable selectors like IDs or data attributes over fragile positional ones.

Why does Customer.io show my in-app message as sent but never opened?

Sent logs when the message queues, not when anyone sees it. A common structural cause: people using an app version whose SDK doesn't support in-app messages will accumulate sends that can never be opened (Send in-app messages docs). Customer.io recommends filtering in-app audiences by platform and cio_sdk_version to exclude them.

Do in-app messages work in single-page applications?

Yes, but only if your site sends page calls manually on route changes. Customer.io fires page calls automatically on page loads, which happens once in an SPA, so page-rule targeting breaks silently without manual calls (Test your in-app messages docs).

How often does Customer.io check for new in-app messages?

Every 10 seconds for people with an active message in the queue, every 60 seconds for people who've received a message in the last 30 days but have none queued, and every 180 seconds before your first message or after 30 days of inactivity (Get started docs).

Sources

David Crowther
Book a free consultation →

On our call, you'll be speaking with David Crowther, founder of NerveCentral.

Our initial consultation is not a sales call—you'll talk, I'll listen and ask questions—then I'll come back to you within 48 hours with our best ideas on how to grow your business.