Skip to Content
Concepts

Concepts

Everything about a survey is data the SDK downloads and evaluates locally. This page explains those building blocks.

Surveys

A survey is an ordered set of questions plus the rules for when, to whom, and how often it shows. A project can have many surveys; each active survey is evaluated independently against every event.

Per-survey presentation settings:

  • dismissible — when false, the survey is required: the SDK hides the close button and blocks swipe/scrim dismissal. Default true.
  • presentationsheet (bottom) or dialog (centered), or inherit the project theme’s default.
  • successMessage — the brief “thanks” text shown on completion. Defaults to a clean built-in message.

Question types

TypeWhat it collectsAnswer value
NPS0–10 recommendation score{ value: number }
CESCustomer Effort Score (e.g. 1–7){ value: number }
CSAT_STARStar rating (e.g. 1–5){ value: number }
CSAT_EMOJIEmoji satisfaction scale{ value: string }
THUMBSThumbs up / down{ value: "up" | "down" }
SINGLE_CHOICEPick one option{ choice: string }
OPEN_TEXTFree text, optional max length{ text: string }

The exact config and value shapes are in the API reference.

Triggers

A trigger is the condition that makes a survey eligible. Any matching trigger on a survey fires it.

  • TypeSINGLE (one event) or SEQUENCE (ordered steps).
  • Steps — each step is an event name plus an optional match: an equality filter on the event’s properties, AND-ed together. {} matches on the event name alone.
  • sequenceWindowSeconds (SEQUENCE only) — the maximum time from the first step to the last. null = no limit.
  • delaySeconds — how long to wait after the condition is satisfied before presenting. 0 = immediate.

Occurrence frequency: warmup & fire-every

Two knobs shape which satisfied occurrence fires, distinct from the per-user cap:

  • warmupCount — ignore the first N times the condition is satisfied.
  • fireEvery — fire on every Nth satisfied occurrence thereafter.

For example, warmupCount: 2, fireEvery: 3 ignores the first two occurrences, then fires on the 3rd, 6th, 9th, …

Audience targeting

audienceMatch filters by user properties: an equality map that must be a subset of the user’s current properties (AND-ed). {} = everyone.

User properties come from your analytics identity (for Amplitude, the identify-set properties snapshot), or from setUser(properties:) for non-Amplitude hosts. See identity.

Sampling

samplePercent (0–100) rolls out a survey to a deterministic fraction of users:

inSample = stableHash(endUserId + ":" + surveyId) % 100 < samplePercent

The decision is sticky — the same user and survey always resolve the same way, with no server round-trip.

Frequency cap

maxPerUserDays is the per-user cooldown: a user won’t see the survey again within that many days. Use 0 for unlimited. The cap is checked during evaluation and re-checked at fire time (after any delaySeconds).

The SDK also refreshes its config on app-foreground, throttled to at most once every 12 hours, so long-lived app sessions can’t keep serving stale surveys.

Eligibility

For a given event, a survey shows only if all of these pass, in this order:

  1. Windownow is within [startsAt, endsAt], if set.
  2. Trigger — an event matches a step (name + match); for SEQUENCE, steps complete in order within sequenceWindowSeconds.
  3. Frequency — the satisfied-occurrence index passes warmupCount + fireEvery.
  4. AudienceaudienceMatch ⊆ the user’s properties.
  5. Sampling — the user is in the sampled bucket.
  6. Per-user cap — nothing shown to this user within maxPerUserDays.

If eligible, presentation is scheduled after delaySeconds, and the cap is re-checked at show time. All of this runs on-device, so it works offline.