Skip to content

Targeting

Targeting rules are set per form in Share → Mobile app. The SDK downloads them with its configuration and evaluates them on the device. Nothing about the user is sent to a server to make the decision.

  1. Your app calls track or screen, or the app launches.
  2. The SDK collects the surveys with a matching trigger. See Track events & screens.
  3. Each of them goes through the eligibility checks below. The first eligible survey is selected.
  4. If the survey has a delay, the SDK waits. When the delay ends it runs the checks again, and for a screen trigger it also checks that the user is still on that screen.
  5. The display guard, if you set one, gets the last say.
  6. The survey loads in the background and appears once it is ready.

Every matched survey that does not make it produces a surveyNotShown callback with a reason, and a line in the debug log.

The checks run in this order. The first one that fails gives the reason.

#CheckSet in the Mobile app tabReason when it failsTriggersshowSurvey
1Tracking is enableddisabledyesyes
2The Survey ID exists and its channel is onChannel switchsurvey_not_foundyes
3No other survey is visible or waiting for its delayanother_survey_visibleyesyes
showSurvey with force stops here and shows the survey
4Targeting is not pausedpausedyesno
5Now is inside the scheduleSchedule: from, untiloutside_scheduleyesyes
6The platform is selectedAudience: platformsplatform_mismatchyesyes
7The app ID is listedAudience: app IDsapp_mismatchyesyes
8The language is listedAudience: languageslocale_mismatchyesyes
9The user is identified, or not, as requiredAudience: identified / not identified usersaudience_mismatchyesyes
10User-attribute conditions holdAudience: conditions, all / anyaudience_mismatchyesyes
11Frequency allows another displayFrequencyalready_completed, frequency_limit, recontact_waityesyes
12The team-wide pause between surveys has passedFrequency: pause between surveysglobal_cooldownyesno
13The install is inside the share of usersAudience: share of userssampled_outyesno

An empty audience setting means no restriction: no platforms selected is every platform, no languages is every language, no conditions is every user.

After selection, three more things can stop a survey: the user left the screen during the delay (screen_changed), the display guard said no (vetoed), or the survey could not be loaded (load_failed, survey_unavailable). All reasons are explained in Testing & debugging.

The platform is ios or android. React Native, Flutter and Unity apps report the platform they run on.

The app ID is the bundle identifier on iOS and the application ID on Android. The comparison is exact and case-sensitive. List app IDs when one team ships several apps, or to keep a survey out of a build with its own ID such as com.acme.app.debug.

The SDK’s language is the value you set with setLocale, or else the device’s preferred language, as a tag like en-US.

An entry in the list matches when it equals the tag or is a prefix of it up to a hyphen. Case is ignored and _ counts as -.

List entryDevice languageMatch
ptpt-BRyes
pt-BRpt-BRyes
pt-BRpt-PTno
pt_brpt-BRyes

A user is identified when a user ID is set, that is after identify and before reset. The rule can require identified users, require users who are not identified, or accept both.

Conditions compare user attributes (the traits from identify and setAttributes) with a value. With all, every condition must be true. With any, one is enough. Property conditions on an event trigger use the same operators against the properties of the track call, and all of them must be true.

OperatorTrue when
existsThe attribute is set.
does not existThe attribute is not set.
equalsThe attribute is set and equal to the value.
does not equalThe attribute is not set, or is different.
containsThe attribute is set and its text contains the value.
does not containThe attribute is not set, or its text does not contain the value.
greater than, greater or equal, less than, less or equalThe attribute is set and compares that way.

How values are compared:

  • Equals. Two numbers compare as numbers and two booleans as booleans. Anything else compares as text, ignoring case. "Pro" equals "pro".
  • Contains. Ignores case. A number or boolean is treated as its text: 5, 49.9, true.
  • Greater / less. Two numbers compare as numbers. So do strings that are plain decimal numbers, such as "12.5". Two other strings compare as text, character by character and case-sensitive. A boolean never compares as greater or less.
  • Dates. A date attribute is stored as text like 2026-11-10T12:00:00Z, which sorts correctly as text. Enter dates in conditions in the same form, for example “signed_up less than 2026-01-01T00:00:00Z”.

The percentage is a stable sample of installs, not a dice roll on every trigger.

  • Each install gets a fixed position from 0 to 99 for each survey, derived from the install ID and the Survey ID. The install is in the sample when its position is below the percentage.
  • The answer for one install never flips between sessions, so a survey does not appear and disappear.
  • Raising the percentage keeps everyone who was already in and adds more installs. Lowering it removes some.
  • Different surveys sample different installs.
  • reset keeps the install ID, so the position stays. Reinstalling the app creates a new install ID and a new position.
  • With few users the real share can differ from the percentage.

The share applies to triggers only. showSurvey ignores it.

Frequency is counted per install. A display is counted when the survey becomes visible, whether the respondent answers or closes it. A survey that never became visible (vetoed, cancelled during the delay, failed to load) is not counted.

ModeShown whenThen
OnceIt has never been shown on this install.Never again, completed or not.
Until completedIt has not been completed, the maximum number of displays is not reached, and the days between displays have passed.Stops for good once completed.
RepeatedlyThe maximum number of displays is not reached and the days between displays have passed.Keeps coming back, also after a completed response.
Every timeThe maximum number of displays is not reached.Appears every time the trigger matches.

Reasons you will see: already_completed (completed, in once or until completed), frequency_limit (already shown in once, or the maximum is reached), recontact_wait (the days between displays have not passed).

One setting applies to the whole team: a number of hours that must pass between any two surveys shown by triggers, whatever the form. It keeps several active surveys from stacking up on the same user. A survey blocked by it reports global_cooldown.

The clock starts when a survey is shown by a trigger. Surveys shown with showSurvey neither wait for it nor restart it.

A survey is eligible from the from time up to, but not including, the until time. Either bound can be empty. The device clock is used.

When one call matches more than one survey, the SDK takes the first eligible one in the order of the configuration and reports the others as another_survey_selected. Do not rely on that order. If two surveys can match the same trigger for the same user, separate them with audience or frequency rules.

Only one survey is on screen at a time. A trigger that fires while another survey is visible or waiting for its delay reports another_survey_visible and is not retried later.

showSurvey is your explicit request, so three checks do not apply to it: pause, the pause between surveys, and the share of users. The delay is skipped as well. Audience, schedule and frequency still apply.

With force, audience, schedule, frequency and the display guard are skipped too. See Show a survey manually.

Display history, event counts and the sampling position are stored on the device, per install.

  • A user with two devices can see a once survey on each.
  • reset (for example on logout) clears display history and event counts, so frequency starts over on that device.
  • Reinstalling the app clears everything.

The SDK caches the configuration for about 10 minutes and refreshes it when the app starts or returns to the foreground with an expired cache. A change you save in the Mobile app tab reaches a device within that time. See Testing & debugging.