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— whenfalse, the survey is required: the SDK hides the close button and blocks swipe/scrim dismissal. Defaulttrue.presentation—sheet(bottom) ordialog(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
| Type | What it collects | Answer value |
|---|---|---|
NPS | 0–10 recommendation score | { value: number } |
CES | Customer Effort Score (e.g. 1–7) | { value: number } |
CSAT_STAR | Star rating (e.g. 1–5) | { value: number } |
CSAT_EMOJI | Emoji satisfaction scale | { value: string } |
THUMBS | Thumbs up / down | { value: "up" | "down" } |
SINGLE_CHOICE | Pick one option | { choice: string } |
OPEN_TEXT | Free 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.
- Type —
SINGLE(one event) orSEQUENCE(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 < samplePercentThe 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:
- Window —
nowis within[startsAt, endsAt], if set. - Trigger — an event matches a step (name +
match); forSEQUENCE, steps complete in order withinsequenceWindowSeconds. - Frequency — the satisfied-occurrence index passes
warmupCount+fireEvery. - Audience —
audienceMatch⊆ the user’s properties. - Sampling — the user is in the sampled bucket.
- 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.