Skip to main content
Supported on: Android SDK 1.0.33+
You can A/B test this survey with no extra code — create an experiment on it in the Console and the SDK serves the assigned variant automatically. See Servable Surface Experiments.
The surveys module lets you collect user feedback through in-app surveys. Surveys can trigger automatically based on events (like in-app messages) or be presented manually from your code.

How It Works

Surveys are configured in the Console with content, question types, and trigger rules. Like in-app messages, they can appear automatically when trigger conditions are met — no code required for trigger-based surveys.

Present a Survey Manually

The SDK uses the foreground Activity captured at configure time to launch its internal SurveyActivity. If the survey ID is unknown or the config hasn’t loaded, the call is a no-op and an SDK log line explains why.

Module Access

Module Methods

9 Question Types

Question types and their content are defined entirely in the Console. The SDK renders them automatically based on the survey configuration.
scale is an accepted alias for likert, and scale_config for likert_config — both SDKs read either spelling, because questions were stored under the older one before likert became authorable. The Console writes the canonical names, so a survey created today uses likert and likert_config.A max above 10 is refused at authoring time: the scale draws as one un-scrolling row of fixed-width buttons, so a wider range would run off the edge of a phone. A min greater than max is read as the range the other way round rather than drawing nothing.

Asking for a store review

A review prompt is never filtered by how someone answered. Google’s policy is explicit — “Don’t ask the user any questions before presenting the rating button or card, including questions about their opinion” — and Apple’s App Review Guidelines say the same for the App Store. An app that screens users first risks rejection.So the SDK never offers a screener, and a survey cannot be configured as one: if any sentiment branch asks for a review, every respondent who finishes the survey is eligible, whatever they answered. The OS decides the rest — Google’s In-App Review API applies its own quota, and it deliberately does not report whether a prompt appeared.
Set prompt_review on whichever branch of follow_up_actions fits your product; it is the ASKING that is configured, not the audience. The respondent’s own branch still runs, so a detractor who lands on show_feedback_form sees the form as well.

Question configuration

Each type reads its own config object on the question. Everything is optional; an unset value takes the default below.
The emoji scale is read in order. The first entry is the worst and the last the best, and that order is what sentiment routing uses — see Follow-up actions. A scale of any length works.

Conditional questions

A question can carry a show_if naming an EARLIER question and the answers that reveal it:
The condition is re-evaluated after every answer, so the question appears and disappears as the respondent moves. Two rules worth knowing:
  • Unanswered means hidden. If the question it references has not been answered yet, the conditional question is not shown.
  • A blank question_id means no condition. A half-configured show_if shows the question rather than hiding it — otherwise a survey could render with nothing in it at all.

When a survey appears

trigger_rules on the survey decides this; no code is needed for a triggered survey. Presenting with present(...) from your own code ignores all of this except the survey’s own status and audience.

Follow-up actions

When a survey completes, the SDK classifies the FIRST answer as positive, negative or neutral, and runs the matching branch of follow_up_actions. A numeric type whose answer is NOT a number falls through to the emoji row, so a csat survey whose first question is an emoji scale is classified by that scale rather than coming back neutral. An answer that is neither a number nor a member of the scale is always neutral.
Sentiment is read from the survey’s survey_type, not from the question’s type. A survey left as custom whose first question is an NPS scale is always neutral, and its branches never run. Set survey_type to match the first question.
Each branch is { action, message }:
A review prompt is never filtered by how someone answered. Apple’s App Review Guidelines and Google Play’s In-App Review policy both forbid asking the user a question — including one about their opinion — before presenting the rating prompt, and both forbid filtering who sees it by the answer.So: if any branch asks for prompt_review, every respondent who finishes the survey is eligible, whatever they answered, and the OS quota decides the rest. Putting prompt_review on one branch configures the ASKING, never the audience. The respondent’s own branch still runs, so a detractor routed to show_feedback_form sees the form as well.

Appearance

appearance on the survey: appearance.theme may be a flat object, or { "light": { … }, "dark": { … } } — in the second form the active variant is sparse-merged over the other, so a dark theme only needs the keys it changes. Plus the rich-media keys in the next section.

AppDNASurveyDelegate

All 3 methods on this delegate fire from SurveyManager alongside the analytics events. Register your delegate via AppDNA.surveys.setDelegate(...).
  • onSurveyPresented(surveyId) — fires when the survey view appears (alongside the survey_shown event).
  • onSurveyCompleted(surveyId, responses) — fires when the user submits the final question. responses is a List<SurveyResponse> (one entry per answered question with questionId + the raw answer), suitable for forwarding to your own analytics or CRM.
  • onSurveyDismissed(surveyId) — fires when the user closes the survey before completing it (back button, swipe-to-dismiss, outside tap).
Implement the delegate to capture survey responses and lifecycle events:

SurveyResponse

Cast answer to the type matching the question (see Question Types).

Example Implementation

Rich Media

Survey questions support rich media content configured in the Console:
Option icons are not authorable. choice_config.options[] accepts an icon through the API and the survey editor has no control for it, so it cannot be set from the Console; it renders on iOS and not on Android. Treat choice options as { id, label } until that is resolved.
See the Rich Media guide for the Lottie, particle and haptic key tables, and for which formats each platform plays.

Auto-Tracked Events

Every event whose name begins survey_ carries survey_id. The two feedback_form_* events are emitted by the form itself and carry only the fields named below.
feedback_form_submitted / feedback_form_dismissed come from the survey follow-up form — the one follow_up_actions opens. There is no separate standalone form.reason on the dismissal is user (the Cancel button, a back press, or a tap outside the dialog) or presenter_released (the host navigated away and the form went with it — a form the user never saw, which would otherwise be counted as one they closed).

Full Example

Surveys are created in the Console under Feedback > Surveys. Trigger-based surveys appear automatically — manual presentation with present() is for precise control over timing.