Skip to main content
Supported on: iOS SDK 1.0.70+ · Android SDK 1.0.42+ · React Native SDK 1.0.7+ (New Architecture only)
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

present returns once the survey request has been dispatched to the native SDK. Whether the survey actually appears depends on the survey’s status, audience, and trigger rules configured in the Console — the SDK fires the survey_shown analytics event (and your delegate’s onSurveyPresented callback) when it does. If you need to confirm that a survey was shown to the user, observe the delegate rather than the return value.

Module Access

Module Methods

9 Question Types

Surveys support the following question types, configured in the Console:
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.

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 the native survey pipeline alongside the existing 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 of { questionId, answer } maps — one entry per answered question — suitable for forwarding to your own analytics or CRM.
  • onSurveyDismissed(surveyId) — fires when the user closes the survey before completing it (X button, swipe-to-dismiss, outside tap).
Implement the delegate to capture survey responses and lifecycle events:

Response Shape

responses is Record<string, unknown>[] — an ordered list, one entry per answered question, each with a questionId and an answer. The type of answer follows the question type:

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; on Android also 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 cases where you want precise control over timing.