Flow In-App vs. Targeting In-App

This guide walks you through what happens after you publish an in-app campaign: how your message reaches the user's phone, how long it takes, and what your application needs to do along the way.

📘

SDK version note

This guide describes the behavior of current SDK versions (Android 6.0.96 / iOS 5.101 and later). If your applications use an older Dengage SDK, some behaviors, especially fetch timing may differ; we recommend keeping the SDK up to date.

1. Dengage Has Two Separate In-App Systems

This is the most important thing to know before you build your first campaign: Flow In-App and Targeting In-App run through the same SDK, but they are two completely separate systems. Both end up showing an in-app message on the user's screen; the difference is where the decision about "who should see it" is made.

Flow In-App

The decision is made on the server, when you publish the campaign. A separate message is produced for each user, placed into a personal mailbox, and delivered once.

Think of it as a personal letter written in your name and dropped into your mailbox. You open the mailbox once, the letter is yours, and the mailbox is emptied.

Targeting In-App

The decision is made on the device, at the moment of display. A single campaign bundle is distributed to all devices; the rules are evaluated on the phone.

Think of it as the neighborhood notice board. Everyone looks at the same board; whether a notice speaks to you is decided by you, on the spot.

Flow In-AppTargeting In-App
Who decidesServer, the audience is computed at publish timeDevice, the SDK evaluates the rules on the fly
Produced per person?Yes, a separate message for each userNo, one bundle for everyone. Personalization is possible, but at display time rather than at production time, see section 5.4
Where it's storedPer user mailbox on the serverSingle per app bundle on the CDN
Delivery semanticsDelivered once, removed from the mailbox once receivedContinuous broadcast, everyone downloads it while it's active
TriggerCampaign send time (segment)The user's behavior at that moment

You can and should use both at the same time; the SDK merges them into a single list and orders them by priority.

The Same Seven Steps, Side by Side

Chapters 4 and 5 follow the same seven steps, so you can compare the two systems step by step.

StepIn FlowIn Targeting
PreparationContent, audience, target apps, expiryContent, rules, target app, date range
Audience resolutionAudience is resolved on the serverA rule set is defined; the decision is left to the device
ProductionPer person messages are renderedAll of the app's active campaigns are compiled into one bundle
DistributionWritten into per user mailboxesPublished via the CDN
DeliveryThe SDK reads and empties the mailboxThe SDK downloads the bundle
SelectionDevice filters + priorityDevice rules + priority
MeasurementDelivered + display/click/dismissDisplay/click/dismiss

2. Concepts You'll See Throughout This Guide

TermMeaning
AccountYour Dengage account. Each account's data is kept fully separate.
AppA platform-based application definition (iOS, Android, Web). Many in-app settings are per app.
ContactA known, identified user. Identified by contactKey.
DeviceAn unidentified (anonymous) device. Identified by deviceId.
SendA single send instance of a campaign.
expireDateThe message's last moment of validity. After this moment it is not shown and is deleted automatically.
priorityWhich message is shown when more than one is eligible at the same time. Lower number = higher priority.
displayConditionWhere / to whom the message is shown: screen name filters, rule set.
displayTimingWhen / how many times the message is shown: delay, repeat interval, maximum display count.
SDK ParametersThe settings package that determines the SDK's behavior. The SDK pulls it from the server and caches it.

3. When Does the SDK Go to the Server? - The Fetch Mechanism

The real answer to "I sent the campaign, when will the user see it?" lives here, and it applies to both systems. Even if your campaign is ready on the server, the message only lands on the device when the SDK goes to the server.

3.1 The Basic Principle

The SDK does not continuously poll the server in the background. Going to the server requires two conditions to hold together:

1) A TRIGGER must occur      (when is it attempted)
          +
2) The FETCH INTERVAL must   (is the attempt allowed)
   have elapsed
          ↓
   the SDK goes to the server

If a trigger occurs but the interval has not elapsed, the call is silently skipped, this is not an error; it is by design. The main exception is foregrounding: launching the app and returning from the background always produce a fetch regardless of the interval (only a 10-second floor is applied, to filter rapid back-to-back transitions). The interval rule applies to the in-session timer.

3.2 Triggers

TriggerDescription
App comes to the foregroundThe dominant trigger. First launch and return from background always produce a fetch, they are not subject to the interval; only the 10-second floor applies. The vast majority of messages arrive at this moment.
SDK settings refreshThe SDK refreshes its settings package at every app start (when the first screen opens); once the settings arrive, a fetch is triggered right after.
In-session timerIf the app stays in the foreground, recurring rounds follow the fetch interval; on empty responses the rounds gradually become less frequent (see 3.4). The timer stops in the background, and is reset and re-armed on foregrounding.
📘

In-app is not a push notification. No in-app message is shown unless the user opens the app, if a push arrives and the user does not tap it and enter the app, no in-app message appears. Events that wake the app in the background (e.g. silent push) do not produce an in-app request either, fetch only runs while the user is actually on screen. A background wake-up also does not count as a session/visit; the session window slides based on inactivity, so visit-based Targeting rules evaluate accurately.

3.3 Fetch Intervals and Where You Configure Them

Fetch typeSettingWhereScope
Flow in-app messagesinAppFetchIntervalInMinPanel → application settingsPer app
Minimum time between messagesinAppMinSecBetweenMessagesPanel → application settingsPer app
Targeting campaign bundlerealTimeInAppFetchIntervalInMinutesSystem-wide configurationGlobal, cannot be changed per app
Visitor information-Fixed inside the SDKFreshness of a few minutes
  • For Flow, how quickly messages are seen is under your control, per app.
  • For Targeting there is no such flexibility; the interval is shared across the whole system.
  • Shortening the interval has a cost: more frequent requests mean more battery and data use on the device.

A commonly used Flow fetch interval is in the neighborhood of 15 minutes; this is not a constant, however, it is an application setting.

📘

Fetch intervals, the minimum time between messages, and validity limits vary by account and application, check the application settings in your panel for the values that apply to you.

3.4 How the Interval Works

After every fetch, the SDK sets a stamp saying "the next fetch is no earlier than this moment". The in-session timer checks this stamp first; foregrounding does not, apart from the 10-second floor, it always fetches.

Example: 15-minute interval

09:00     App launched            → FETCHES (every launch is unconditional)
09:03     Background → foreground → FETCHES (10-second floor has elapsed)
09:03:05  Another quick switch    → 10-second floor → SKIPPED
09:15     User is in the app      → in-session round → FETCHES

Gradual back-off on empty responses: as in-session rounds keep coming back empty, the SDK gradually spaces them out; the ceiling is the greater of your fetch interval and 15 minutes. When a non-empty response arrives, the cadence returns to normal; foregrounding resets the back-off entirely. Users with a waiting message never feel this delay, foregrounding fetches unconditionally anyway.

On Error

  • A failed request also consumes the window; the next attempt happens when the interval elapses or at the next foregrounding.
  • While a request is in flight, a second one of the same type is not sent.

3.5 What Bypasses the Interval and What Doesn't

SituationBypasses?Why
The user keeps navigating inside the appNoWhile a session is running, the only fetch path is the in-session timer, and it is subject to the interval; on empty responses the rounds can space out up to the back-off ceiling (see 3.4). Screen transitions (setNavigation) do not trigger a fetch, they only evaluate the display of messages already on hand.
The app is brought to the foreground from the backgroundYesForegrounding is not subject to the interval; only the 10-second floor applies (0 in development mode).
The app is fully closed and reopenedYesThe first fetch of every launch is unconditional, the most reliable path for testing.
The app is uninstalled and reinstalledYesThe local store is reset, but so is all in-app state on the device (display counts, message list).
Development mode is onYesDisables the intervals. See below.
The device is on the panel's "debug device" listYesListed devices behave like development mode: intervals and the floor are bypassed, and debug logs are produced in addition.

Development Mode

You can enable development mode in two ways: from the application code (Android setDevelopmentStatus(true); on iOS, marking the environment as development) or from the panel, by adding the device to the debug device list (debugDeviceIds), the device adopts the mode at the next SDK settings refresh (app launch); no build change is required.

In development mode the SDK does not apply: the Flow fetch interval, the Targeting fetch interval, the 10-second foregrounding floor, or the minimum time between messages. In other words, every trigger really goes to the server, and messages can be shown back to back. When testing, this is essential.

The panel debug list also produces a step-by-step debug log of rule evaluation. Remember to remove test devices from the list before going live, listed devices skip all intervals.

3.6 Stale-Data Protection: Shared by Both Systems

Before showing a message, the SDK checks when the last successful fetch happened. The threshold: 4× the fetch interval, with a minimum of 60 minutes.

The rule is: if the last successful fetch on both the Flow side and the Targeting side is older than this threshold, no message is shown at all, neither Flow nor Targeting. If either one is fresh, there is no blocking.

The purpose is to prevent a device that has been offline for a long time from showing a campaign that has long since been cancelled or expired. Once connectivity returns and the first fetch succeeds, everything returns to normal.

3.7 How the Message List on the Device Is Preserved

  • Messages downloaded to the device are kept in persistent storage, they are not lost when the app is closed.
  • When a new fetch arrives, the list is not reset but merged: if the same message arrives again, its display count, next-display time and dismiss count are preserved.
  • For a new message not present in the list, these values are restored from the display history kept on the device. Even if a message is deleted and downloaded again, the fact that "this user has seen it 3 times" is not lost.
  • The display history is retained for a limited period and cleaned periodically.

3.8 Device Cleanup: Expired and Cancelled Campaigns

The local list on the device is kept tidy in two ways:

  • Expired messages are filtered out on the device via expireDate, they stop showing the moment their validity ends and are removed automatically.
  • Cancelled campaigns are removed shortly after cancellation: with each fetch, the SDK also learns which sends have been cancelled and drops the matching messages from its local list.

What to expect: a cancelled campaign is cleaned off the device at the device's next fetch.

4. Flow In-App: From Publish to Screen

4.1 Step 1 · Preparation: Set Up the Campaign in the Panel

  1. Content: an HTML-based template (modal, full-screen, banner, inline, story, countdown). Define your personalization variables here.
  2. Target applications: multi-select. Determines which "mailboxes" the message will be written into.
  3. Target audience: segment / list / filter.
  4. Validity: a fixed date, or "N days/hours/weeks from send".
  5. Display settings: priority, displayTiming, displayCondition.
  6. Send time: immediately or scheduled.
📘

The target application list intersects with the target audience. If a contact is in the audience but has no registered device in the selected applications, no message is created for that user.

4.2 Step 2 · Audience Resolution: Who Gets the Message Is Decided on the Server

When you publish, the campaign turns into a send order. When it is due, the audience is resolved: for every contact/device in the target segment, a personal message is prepared.

What to expect: from seconds to minutes depending on audience size. For audiences in the millions, the first messages start flowing while the rest are still being produced, the process is a stream.

4.3 Step 3 · Production: Messages Are Personalized

For each target user, the content template is rendered with the personalization variables and prepared for delivery. Content that is identical for everyone is stored once and attached at delivery time, so even very large sends stay fast.

4.4 Step 4 · Distribution: Messages Are Written into User Mailboxes

Each message is written into the user's own "mailbox" on the server.

BehaviorWhat it means for you
Per-app isolationA separate mailbox is used for each target application. A message shown on iOS can also appear on Android, each platform reads from its own mailbox.
Device intersectionFor contact records, the campaign's target applications are intersected with the applications where the user actually has a device. If the intersection is empty, no message is created. This is the most common cause of "the audience had 1M people but only 600K messages were created".
Per-message validityEvery message lives with its own expireDate. An expired message is deleted on its own; the others remain.
ReliabilityMessages are not lost during temporary hiccups, they are retried until they are safely written.

What to expect: distribution typically takes seconds.

4.5 Step 5 · Delivery: The Message Moves from Server to Device

  1. The SDK's fetch request is validated (account, application, in-app enabled).
  2. The mailbox is read and emptied in a single operation, the same message is never delivered twice.
  3. Delivered messages are recorded as delivered; this feeds the delivered metric you see in reports.

If a message cannot be completed at delivery time, it is not lost, it stays in the mailbox and is delivered on a later fetch. A read-only counterpart, peekMessages, shows the mailbox contents without emptying it (useful for diagnostics, see Chapter 8).

4.6 Step 6 · Selection: The Device Decides Which Message to Show

The display decision is made when setNavigation() is called, which means your application must inform the SDK on every screen transition.

  1. Stale-data protection (see 3.6)

  2. Expiry filter: messages past their expireDate are discarded.

  3. Minimum time between messages: inAppMinSecBetweenMessages.

  4. Screen filter: evaluated in two passes. Screen name matching (combinable with AND/OR), but elimination is not single-pass:

    SituationWhat is considered
    No screen name providedOnly messages without a screen filter
    Screen name providedFirst, messages with a screen filter that matches
    Screen name provided, none matchFall back to messages with no screen filter at all

    In practice: setting up a screen-filtered campaign does not prevent unfiltered campaigns from appearing on that screen, it only takes precedence over them.

  5. Display timing validation: maxShowCount, showEveryXMinutes.

  6. Priority ordering:

    • Flow messages come before Targeting messages

    • Then priority , lower number first

    • If priorities are equal, the remaining ties are resolved automatically (rule set and expiry are considered)

      📘 ### When it matters which of two messages appears first, give them distinct `priority` values rather than relying on tie-breaking.
  7. Delay: displayTiming.delay.

  8. Special content types: inline, story, countdown, coupon messages. If a coupon cannot be allocated, the message is not shown.

After display: if showEveryXMinutes is defined, the message stays in the local list and is scheduled for the next round. If it is not defined, the Flow message is deleted, that is, a Flow message is shown once by default. For the full semantics of the frequency settings, see 6.4.

4.7 Step 7 · Measurement: Events Are Reported

EventWhen
DeliveredWhen the server delivers the message to the SDK
DisplayedWhen the message appears on screen
ClickedWhen the button / message is tapped
DismissedWhen the user closes it

4.8 Test Sends

When you use "send test" in the panel, the audience resolution step is skipped: the message is prepared immediately. What to expect: the test message lands in the mailbox within seconds; when it appears on the device depends entirely on the fetch mechanism.

  1. Install a build with development mode enabled on the test device, the fetch interval and the between-messages wait are disabled.
  2. If the message does not arrive, fully close (force-quit) the app and reopen it, the first fetch of every launch is unconditional; this is the most reliable path. If you test with background/foreground cycles, leave at least 10 seconds between two foregrounds, a transition made before the floor elapses produces no fetch and creates the false impression that "the message didn't arrive". In development mode and for devices on the panel debug list this floor is 0.
⚠️

A test send cannot be delivered to a contact that does not have a registered device. Before sending a test, make sure the subscription process has been completed for that contact, and send the test to the app where the subscription was created.

⚠️ What a Test Send Validates and What It Doesn't

A test send is a content preview, not a delivery simulation. The payload is built from scratch, and the campaign's display settings are not carried into it, default/fixed values are used instead.

Validates ✅Does not validate ❌
The content HTML (with personalization variables rendered)displayTiming : delay, repeat interval, postpone allowance: all defaults
The screen name filter you type on the test screenThe campaign's displayCondition rules: sent empty
The message reaching the device and the SDK integrationValidity: fixed at 1 hour, not the campaign's expire setting
The template rendering correctly on the devicePriority: fixed, not the campaign's priority setting

In practice this means: nothing about display behavior can be tested with a test send; delay, repetition, "remind me later", screen filters, priority races. Make a real send for those. Concluding "it doesn't work" from a test send is the most common false alarm.

4.9 Campaign Cancellation

  1. Server cleanup: the messages belonging to the cancelled send are removed from the mailboxes; messages not yet delivered never go out.
  2. Device cleanup: the SDK learns about the cancellation and drops the messages it has already downloaded (see 3.8).

Cancellation cannot take back messages that have already been displayed.

4.10 The Flow In-App Pipeline at a Glance

PREPARATION   Panel: campaign + audience + target apps + expire
     │
     ▼  publish
AUDIENCE      Send order ──► audience resolution (streaming)
     │
     ▼
PRODUCTION    Personalization + content rendering
     │
     ▼
DISTRIBUTION  Per-user mailboxes, per-message validity
     │          └─ for contacts: target apps ∩ apps with a device
     ▼
DELIVERY      SDK ──fetch──► mailbox is read and emptied,
     │                       delivery is recorded
     ▼
SELECTION     Device: local list → expire/screen/timing filters
     │        → priority → delay → SHOW
     ▼
MEASUREMENT   displayed / clicked / dismissed → reports

5. Targeting In-App: From Publish to Screen

5.1 Step 1 · Preparation: Set Up the Campaign in the Panel

In a Targeting campaign you do not select an audience. Instead, you define:

  1. Target application(s): which application's broadcast bundle the campaign will enter.
  2. Content: the same template infrastructure as Flow.
  3. Rule set: the conditions to be evaluated on the device (see 5.2).
  4. Screen filters: on which screen(s) it applies.
  5. Start / end date: the campaign stays live within this range.
  6. Display settings: priority, displayTiming. In Targeting, showEveryXMinutes is used much more commonly.
  7. Activation: set the campaign to active or passive. Targeting in-app campaigns have no approval mechanism; activating the campaign is enough for it to go live. (In Dengage, the approval process applies to campaign Flows and contents; Targeting in-app campaigns are not part of it.)

5.2 Step 2 · Targeting: The Decision Is Left to the Device

GroupParameters
BehaviorCart item count, cart total, category path, last viewed product and its price, current page type/title, event history
VisitPages seen in the visit, visit duration, first visit or not, visit count, last visit time
IdentityAnonymous or not, segment, tag, contact attributes
Device / contextOS and version, brand/model, screen dimensions, language, timezone, country/city/region, push permission
TimeHour, day of week, month
Free-formValues your application passes via setNavigation

Some of these parameters are known directly on the device (device, time, screen); some come from the calls your application reports to the SDK (cart, page, events); segment/tag/contact attributes are retrieved from the server with a separate visitor-information call (see 5.7).

5.3 Step 3 · Production: The Application's Bundle Is Compiled as a Whole

Targeting's unit of publication is not the campaign but the application. Each application has a single broadcast bundle, and that bundle is the complete list of all campaigns currently live for that application.

When a campaign is added, edited, stopped, or expires, the bundle is regenerated as a whole. The device downloads a single file and learns "everything that is live right now", so there are no missed updates and no drift between devices. A few practical consequences:

  • Even a small edit republishes the bundle; allow a short propagation time (see 5.5).
  • One campaign with a content problem does not affect the others in the bundle.
  • Delivery cost does not depend on how many users you have, everyone downloads the same file.

5.4 Personalization: How It's Done in Targeting

The Targeting bundle arrives identical on every device. There is no per-person render step while the bundle is prepared, so contact variables written into the template (name, birth date, etc.) are not filled in when the bundle is compiled.

Personalization happens instead on the content side, at display time. The SDK provides a bridge to the in-app content; through this bridge the content can fetch data from the server at the moment of display and fill the fields on screen. This means the message uses the data from the moment the user sees it, not from the moment the bundle was compiled.

Which Path, When

ApproachWhen it fits
Rule-based splittingThe simplest path. If a few variants are enough ("A to those with a cart above 500 TL, B to the rest"), set up separate campaigns; no content development needed.
App-supplied valuesYour application hands any key-value pairs to the SDK with setInAppDeviceInfo(key, value); the content template uses them directly. No network call required.
CouponsPer person code distribution. The SDK allocates the coupon before display and injects it into the content.
Live data on the content sideWhen a truly personal value must be shown (name, points, amount, status). Requires development in the content template.
Flow In-AppWhen personalization is central to the campaign and delivery/reporting guarantees are also wanted.

App-Supplied Values, dnInAppDeviceInfo

The simplest and most overlooked path. Your application leaves values it already knows with the SDK; the SDK hands them to the content template at display time.

Application:
  Dengage.setInAppDeviceInfo("firstName", "Alex")
  Dengage.setInAppDeviceInfo("points", "1240")

Template:
  Hello {{dnInAppDeviceInfo.firstName}}
  You have {{dnInAppDeviceInfo.points}} points

You define the keys, there is no fixed list. The same API exists on both platforms (setInAppDeviceInfo, clearInAppDeviceInfo, getInAppDeviceInfo).

  • Set the value before the message is displayed; otherwise the field renders empty.
  • Values are stored persistently. If you do not call clearInAppDeviceInfo() on logout, the previous user's name is shown to the next user, this is the most likely source of error.
  • For data your application does not know (e.g. the current points balance on the server), this path is not enough, use live data instead.

Example Scenarios Possible with Live Data on the Content Side

  • "Hello Alex" , greeting the user by name
  • "You have 1,240 points 260 more to reach Gold", loyalty status
  • "10% off the 3 items in your cart", the cart at that moment
  • "A price just for you: 349 TL" , personal pricing
  • "Your order has shipped", order status
  • "Picked for you", a personalized recommendation list

This approach requires development in the content template; since the data is fetched at display time, the content must also handle loading and error states. It also requires an up-to-date Dengage SDK. Assess feasibility per campaign together with your mobile team.

5.5 Step 4 · Distribution: The Bundle Is Published via CDN

The bundle is published to the CDN, and devices pick up the new version as it propagates.

What to expect: publishing takes seconds; total latency is determined by CDN propagation, typically seconds to a few minutes.

5.6 Step 5 · Delivery: The SDK Downloads the Bundle

The SDK downloads the application's bundle from the CDN. There is no per user query, every device receives the same file. Download conditions are governed by Chapter 3; the interval setting is system-wide.

SDK Compatibility

Not every device in the field runs the newest SDK. Campaigns that use the newest capabilities are delivered only to devices whose SDK can evaluate them correctly; older SDKs simply do not receive those campaigns. This prevents a campaign from being shown to the wrong people, the design prefers silent exclusion over wrong display. Capabilities that require an up-to-date SDK include:

  • Rule criteria such as last viewed product id/price, last category path, current page title/URL/type, cart contents, and event history
  • Coupon sections in the content
⚠️

If your campaign uses one of these capabilities, users whose devices run an outdated SDK never see that campaign. If a campaign report shows lower reach than expected, check the SDK version distribution of your user base.

5.7 Visitor Information

If your rules use segments, tags, or contact attributes, the SDK retrieves this information with a separate call and caches it on the device with a freshness of a few minutes. If a user has just joined a segment, the campaign may not be visible to them until this information is refreshed. This information is used only in rule evaluation, not in content rendering (see 5.4).

5.8 Step 6 · Selection: The Device Evaluates the Rules

When setNavigation() is called, the SDK evaluates every campaign it has downloaded, one by one: is the date range valid, does the screen filter match, does every criterion in the rule set match the current value, is displayTiming satisfied, then the winner is chosen by priority order (see 4.6).

After display, a Targeting message is not removed from the local list, as long as the campaign remains in the bundle, it can be shown again whenever the conditions are met. This is the most visible behavioral difference from Flow.

📘

Define maxShowCount = 1 if you want a Targeting message to appear only once. The default in Targeting is unlimited; the limit is yours to set. Details and caveats: 6.4.

5.9 Step 7 · Measurement: Events Are Reported

Targeting display, click, and dismiss events are reported together with the session, campaign, and content identity. First-open and foregrounding signals are also sent; these feed rules such as first visit and visit count.

5.10 The Targeting In-App Pipeline at a Glance

PREPARATION   Panel: campaign + rules + target app + date range
     │
     ▼  save / activate
TARGETING     Rule set is defined — the decision is left to the device
     │
     ▼
PRODUCTION    Compile: ALL active campaigns of the app → one bundle
     │
     ▼
DISTRIBUTION  Published via CDN (propagation: seconds – minutes)
     │
     ▼
DELIVERY      SDK ──bundle──► one file for every device
     │        SDK ──visitor info──► segment, tag, contact attributes
     ▼
SELECTION     Device: rule evaluation (cart, screen, visit, device, time…)
     │        → priority → SHOW
     ▼
MEASUREMENT   display | click | dismiss → reports

6. Comparison and Selection Guide

6.1 Side by Side

DimensionFlow In-AppTargeting In-App
Targeting decisionOn the server, at publish timeOn the device, at display time
Content personalizationFull, rendered per user on the serverIndirect, not done at bundle compile time; via app-supplied values, rule-based variants, coupons, or live data (see 5.4)
Targeting signalsCRM / segment data (full data)Live signals on the device + segment/tag
Time to go liveDepends on audience sizePublish + CDN propagation
Fetch interval controlPer app, under your controlSystem-wide, cannot be changed
Delivery guaranteeHigh, stays in the mailbox until deliveredNone, there is no concept of "missing" it
Repeat display (default)Once, repeatable with showEveryXMinutesUnlimited, capped with maxShowCount (= 1 → once). Details: 6.4
Audience scaleAny size, messages are produced per userAny size, every device downloads the same bundle
Anonymous usersPossible, but requires device registrationNatural
SDK version sensitivityLowHigh, campaigns using the newest criteria/coupons do not reach outdated SDKs
Data held on the deviceOnly the messages sent to that userAll active campaigns of the application
Cancel / rollbackServer cleanup + device cleanupDeactivate + republish
ReportingDelivered + Displayed + Clicked + DismissedDisplayed + Clicked + Dismissed
Priority raceFlow wins when both are eligibleSteps in when there is no Flow message

6.2 Which Should You Choose?

Choose Flow In-App if:

  • The message text will contain person-specific information (name, order number, a specific product)
  • Targeting is based on CRM / segment data
  • You need "how many people did we deliver to" reporting
  • The message must be delivered exactly once, with certainty
  • You are distributing single-use coupons/codes

Choose Targeting In-App if:

  • The trigger is the user's behavior at that moment
  • The audience includes anonymous users
  • The same message should be shown again whenever the conditions are met
  • You want an "always on" campaign
  • The audience is very large and the content has no person-specific text
  • You want to switch it on/off quickly or A/B test

Personal values can be shown in Targeting too, but that requires development on the content side (see 5.4). If personalization is central to the campaign and you also want a delivery report, Flow is the better fit. If personalization is a small part of the message (a name, a points balance) and the campaign itself must be behavior-triggered, Targeting + live data on the content side is the better choice.

6.3 Scenario Examples

ScenarioRecommendedWhy
Reminder to a user who abandoned their cartTargetingThe trigger is the instantaneous device state
Personal discount code for users on their birthdayFlowPersonal code + CRM data + single use
New version announcement, everyone, for 2 weeksTargetingNo personalization, continuous broadcast
Gold segment with no purchase in 30 daysFlowThe segment is computed on the server; a delivery report is needed
Onboarding for first-time openersTargetingFirst-visit information lives on the device; not even a contact is required
Last call 1 hour before the deadline, to those who clickedFlowTargeting relies on past engagement data
Coupon for users lingering on the product detail screenTargetingThe screen + duration combination is only known on the device
Sign-up prompt for anonymous usersTargetingAnonymity is known on the device
"Hello {name}" personalized recommendations to millionsFlowTemplate-variable personalization is only done in Flow (for Targeting, see 5.4)
Store opening announcement in a specific cityTargetingThe city is known on the device. If person-specific text is needed, Flow

6.4 Display Frequency: How to Control It in the Two Systems

Frequency control uses the same two settings in both systems; the difference is in the default behavior.

SettingValueMeaning
maxShowCountempty / 0 / -1Unlimited
maxShowCountNShown at most N times per device
showEveryXMinutesempty / 0 / -1No repeat interval
showEveryXMinutesNAt least N minutes must pass between two displays

Flow: once. After the message is displayed it is deleted from the device's list → naturally once. If you want repetition, define showEveryXMinutes.

Targeting: unlimited. As long as the campaign remains in the bundle it is not deleted from the list → naturally unlimited. If you want a limit, define maxShowCount.

Common Recipes

You wantmaxShowCountshowEveryXMinutes
Show only once in Targeting1empty
Once a day in Targetingempty1440
At most 3 times, 1 hour apart, in Targeting360
Once in Flow-Show once in the panel
Every other day in Flow-Show every in the panel, 1 day
"At most 3 times" in FlowNot possibleUse a shorter validity (expire) to limit repeats indirectly

The panel presents repetition for a Flow campaign not as a number but as a binary choice:

  • Show once → the message is deleted from the device after display; it appears once
  • Show every … → the message repeats at the chosen interval until expireDate is reached

Consequently, a count-based limit like "show 3 times" is only possible in Targeting (maxShowCount); in Flow, repetition is limited by the validity period.

Things to keep in mind:

  1. The counter is per device, not per user. If the same person signs in from two devices, they can see the message twice. In Flow there is no such risk, because the mailbox is per person + application.
  2. Uninstalling and reinstalling the app resets the counter, like all in-app state on the device.
  3. If a campaign is deactivated and reactivated, the message drops off the device's list; when it comes back, the counter is restored from the display history. If the campaign is republished after a long gap, the counter may start fresh.

6.5 "Remind Me Later" and maxDismissCount

Some in-app templates include a third "Remind me later" button in addition to the normal action and close buttons. maxDismissCount is the counter side of this feature, it is not a standalone "dismiss limit".

Where You Configure It

WhereWhat you do
Content editorGive the third (tertiary) button the Dismiss action type; label it e.g. "Remind me Later"
Send settingsSelect Show every; the two fields that appear under it govern the behavior
Panel fieldFunction
Show every N hoursHow much later the message is shown again
Max dismiss countHow many times the user can postpone

How It Works

  1. The message is displayed → the SDK schedules the next display per the Show every interval and keeps the message in the local list.
  2. The user taps "remind me later" → the postpone counter increments, and the message is not deleted.
  3. When the time elapses, the message is shown again.
  4. When the counter reaches Max dismiss count, the message is deleted permanently.

The postpone interval is the campaign's Show every interval, there is no separate postpone duration.

Closing is different. The content editor has separate Close Button and Dismiss On Touch Outside options. These do not increment the postpone counter and delete the message unconditionally, which is correct behavior by design.

⚠️ Important: Max Dismiss Count Defaults to 0 and 0 Does Not Mean Unlimited

A value of 0 is treated as "delete on the first tap". So if you select Show every but leave Max dismiss count at its default of 0, tapping "remind me later" does not postpone the message, it deletes it. When using the button, always set Max dismiss count to 1 or higher.

Also note:

  • With Show once, the message is deleted at display anyway, the "remind me later" button becomes meaningless in that mode. Campaigns using the button must select Show every.
  • A test send does not carry the campaign's display settings (see 4.8), including Max dismiss count. Validating the postpone behavior requires a real send.
  • In Targeting, the postpone counter does not apply: Targeting messages stay in the local list anyway and simply follow the maxShowCount / showEveryXMinutes limits.

6.6 When Using Both Together

  • Priority: If both are eligible on a screen, the Flow message is shown.
  • Frequency: inAppMinSecBetweenMessages is a shared ceiling for both systems.
  • Design advice: Give Targeting campaigns that stay live continuously a high priority number (low priority) so they do not suppress Flow messages.
  • Monitoring: For Flow, watch the delivered → displayed conversion. A large gap usually means "the message was delivered but the application did not call setNavigation".

7. What Your Application Must Do

7.1 Mandatory Steps

StepWhy it is critical
Initialize the SDK, configure the API endpointsThe in-app, event, and Targeting endpoints are configured separately
setContactKey, set on login, clear on logoutFlow messages are addressed by the user key. Wrong/empty key = no messages
setNavigation on every screenDisplay is evaluated only on this call. If it is not called, even a downloaded message never appears
Write screen names exactly as in the panelScreen filters rely on exact matching
Enable development mode in development buildsThe only mechanism that makes the test cycle possible (see 3.5). Must be off in production builds

7.2 Feeding the Targeting Rules

CallWhat it feeds
pageViewPages seen in the visit, page type/title, last category
setCart · addToCart · viewCartCart item count, cart total, cart contents
setCategoryPathCategory path
setCity · setStateCity / region
sendCustomEvent · order · searchEvent-history-based rules
setTagsTag rules
setNavigation(..., params)Free-form parameters usable in rules

7.3 Optional Capabilities

  • Inline in-app: a message area embedded in the screen; matched via propertyId.
  • Story: a story list component; the seen state is kept on the device.
  • Test page: an SDK screen showing the messages currently on the device and their states.
  • Deeplink: deeplinks on message buttons are passed to your application.

7.4 Common Integration Mistakes

  1. setNavigation is called only on the home screen → no message ever appears on other screens. This is the most frequent problem.
  2. Screen name inconsistency → ProductDetail in the panel, product_detail in the app.
  3. The contact key is not cleared on logout → the previous user's messages are shown to the new user.
  4. Expecting messages before device registration completes → if the contact has no device in the target application, the Flow message is never created.
  5. Testing with development mode off → fetch intervals can block your test cycle. Fully close and reopen the app (every launch fetches) or enable development mode.
  6. inAppMinSecBetweenMessages set too high → the second message never appears in the same session.
  7. Writing contact variables into a Targeting template → they are not filled at bundle compile time and stay empty. Use one of the paths in 5.4 for personalization.

8. Troubleshooting: "The Message Doesn't Appear"

Work through the steps in order; each step assumes the previous one.

  1. Was the campaign really published?
    • Flow: Is the send status complete? Is the created message count in line with expectations?
    • Targeting: Is the campaign active? Is it within the date range?
  2. Does the audience / rule cover this user?
    • Flow: Is the user in the segment? And do they have a registered device in the target applications?
    • Targeting: Does the device actually have a value for every criterion? If a segment/tag rule is used, has the visitor information been refreshed?
  3. Is the message in the server-side mailbox? (Flow only)
    • Check with peekMessages, it shows the mailbox without emptying it. If the message is there, the problem is on the device side; if not, the issue is in production/targeting.
  4. Did the device download the message? (the most frequent root cause)
    • Did the app come to the foreground? Fully closing and reopening the app always produces a fetch (see 3.5); if the message still does not arrive, the problem is not the interval.
    • If testing with background/foreground cycles: was there at least 10 seconds between the two foregrounds? A transition before the floor elapses produces no fetch (the floor is 0 in development mode / on the panel debug list).
    • Is the contact key correct? Is the application identity correct?
    • Targeting: Has the CDN propagation completed?
    • Targeting: Does the campaign use a capability that requires an up-to-date SDK, and is the SDK on the device current? (see 5.6)
  5. Could the device have filtered the message out?
    • Is expireDate past? Does the screen name match the filter? Is maxShowCount exhausted? Has the showEveryXMinutes interval arrived? (These counters survive app restarts, see 3.7)
    • Could inAppMinSecBetweenMessages be suppressing it? Could stale-data protection have kicked in? (see 3.6)
    • Was another, higher-priority message shown instead? For coupon messages: could the coupon allocation have failed?
  6. Could cancellation / cleanup have intervened?
    • If the campaign was cancelled or has expired, the device may have discarded the message (see 3.8).
  7. The report shows delivered but no displayed
    • The message arrived but the display conditions were never satisfied, most often a screen filter or a missing setNavigation.

Diagnostic Tools

ToolWhat for
peekMessagesSeeing the mailbox contents without disturbing it
Development modeDisabling fetch intervals and the between-messages wait for fast testing
Panel debug device listPutting a device into development mode remotely (intervals bypassed) + a step-by-step record of rule evaluation
SDK test pageThe current message list on the device

9. Timing Expectations

StepFlow In-AppTargeting In-App
Publish → message/bundle readySeconds (small) – minutes (audiences in the millions)Seconds
Ready → available for devicesSecondsCDN propagation: seconds – a few minutes
Available → on the deviceNext fetch, foregrounding (instant) / in-session round (per-app interval)Next fetch, foregrounding (instant) / in-session round (system-wide interval)
On the device → on screenNext setNavigation + displayTiming.delaySame
Test sendSeconds (audience step skipped) + fetchVia preview
Cancel → removed from the deviceAt the device's next fetchRepublish + next download

The biggest item in this chain is usually the user's next app open, the message comes down at the moment of opening, without waiting. For users who stay inside the app, the fetch interval is the deciding factor; for Flow you can manage it from the application settings.

9.1 What You Want → Which Setting

If you wantWhat to do
The message to arrive as fast as possibleOn foregrounding the message already arrives instantly. For users staying inside the app, lower (minimum interval for fetching messages) inAppFetchIntervalInMin Raise (minimum duration between displaying messages) inAppMinSecBetweenMessages. Configure here: iOS · Android · Huawei
The user not to see messages back to backRaise inAppMinSecBetweenMessages. Note: if several messages are eligible at once, priority ordering decides and the highest-priority one is shown. But a higher-priority message arriving after another was shown still waits out this interval, regardless of its priority Configure here: iOS · Android · Huawei
The message to appear only onceFlow: Show once. Configure dismiss behaviour · Targeting: maxShowCount = 1 Configure delivery settings.
The message to repeat at intervalsFlow: Show every N Configure dismiss behaviour · Targeting: showEveryXMinutes = N Configure delivery settings.
A limited number of repeatsTargeting: maxShowCount. Not directly possible in Flow, limit indirectly by shortening the validity period
The user to be able to say "remind me later"Dismiss type on the third button in content + Show every + Max dismiss count ≥ 1 in the send settings (see 6.5)
The message to appear on specific screensA displayCondition screen filter; make sure the app calls setNavigation on that screen
The display to be slightly delayeddisplayTiming.delay
Person-specific information in the textUse Flow (see 5.4 for the Targeting alternatives)
A reaction to live behaviorUse Targeting
A faster test cycleAdd the device to the panel debug device list, or install a build with development mode on (see 3.5); background/foreground cycles or fully closing and reopening produce a fetch every time. In normal mode leave at least 10 seconds between two foregrounds

9.2 Worked Scenarios

The examples below are for an application configured with inAppFetchIntervalInMin = 5 minutes and inAppMinSecBetweenMessages = 300 seconds (5 minutes).

📘

Important: These two counters run in parallel; they do not add up. The fetch stamp is set at every fetch, the display stamp at every display, separately.

Scenario A: Single message; the user opens the app

09:00  Fetch happened; no message on the server
09:01  Campaign published; message ready
09:04  User opens the app → fetch (unconditional) → message arrives
09:04  setNavigation → wait out the delay → DISPLAYED

Result: at the next open. Since foregrounding is not subject to the interval, the message arrives at the first open after publishing; the fetch interval creates no wait in this scenario. The between-messages interval does not come into play either, no message was shown before.

Scenario B: Two messages arriving in the same round

09:05  Fetch → A and B arrive together
09:05  A displayed → next display allowed at 09:10
09:07  Screen change, setNavigation → B eligible but SUPPRESSED until 09:10
09:10  Next setNavigation → B DISPLAYED

Result: B five minutes after A. The between-messages interval is decisive. B also needs another setNavigation call to be displayed, it does not pop up on its own when the timer elapses.

Scenario C: The user is already in the app; the message is published meanwhile

09:00  User opens the app → fetch; nothing there
09:03  Campaign published; message ready
09:05  In-session round (interval: 5 min) → fetch → message arrives
09:05  setNavigation → DISPLAYED

Result: ~2 minutes. The in-session round follows inAppFetchIntervalInMin. If empty rounds had accumulated beforehand, the round may have spaced out somewhat (see 3.4); if the user backgrounds and foregrounds the app, the message still arrives instantly.

Scenario D: The message is published right after a fetch

09:00  A displayed → display allowed again at 09:05
09:06  Campaign B published
09:06  User opens the app → fetch → B was not ready yet; came back empty
09:08  Background → foreground → fetch → B arrives → DISPLAYED

Result: at the next foregrounding. An empty fetch does not "lock" the window, the next foregrounding (beyond the 10-second floor) tries again; if the user stays inside the app, B arrives on the next in-session round. The display-side wait (inAppMinSecBetweenMessages) applies separately.

9.3 Common Situations

SituationCause
"The message arrived but was displayed late"Arrival and display are separate: it may be waiting on inAppMinSecBetweenMessages or on a new setNavigation (Scenario B)
"I'm sitting in the app and the message never comes"The in-session round follows the interval but can space out up to the back-off ceiling on empty rounds (see 3.4). Backgrounding and foregrounding the app produces an instant fetch
"I sent a test and it didn't land on the device"Fully close and reopen the app, every launch produces a fetch (see 3.5). If it still doesn't arrive, check the contact/application identity and the targeting (see 4.8). Adding the device to the panel debug list speeds up testing
"The message arrived but doesn't show"setNavigation isn't being called, the screen filter doesn't match, or the between-messages interval is suppressing it (see Chapter 8)
"The second message never appeared"A new setNavigation is needed after the interval elapses; it is not shown on its own when the timer expires
"No in-app has appeared for an hour"Stale-data protection may be active, if the last successful fetch is older than 60 minutes, all display stops (see 3.6)

Did this page help you?