The surveys module lets you collect user feedback through in-app surveys. Surveys can trigger automatically based on events or be presented manually from your code.
Present a Survey Manually
val presented = AppDNA.surveys.present("nps_q1_2026", activity)
if (!presented) {
println("Survey not found or already completed")
}
Returns false if the survey is not available (not found, already completed, or config not loaded).
Module Access
val surveys = AppDNA.surveys
Module Methods
| Method | Signature | Description |
|---|
present | present(surveyId: String, activity: Activity): Boolean | Present a survey manually |
promptReview | promptReview(activity: Activity) | Start the smart review prompting flow |
setDelegate | setDelegate(delegate: SurveyDelegate?) | Set a delegate for survey callbacks |
8 Question Types
| Type | Display | Response |
|---|
nps | 0-10 numeric scale | Integer 0-10 |
csat | 1-5 satisfaction scale | Integer 1-5 |
rating | Star rating | Integer 1-5 |
emoji_scale | Emoji options | Selected emoji value |
yes_no | Binary choice | Boolean |
single_choice | Radio button options | Selected option string |
multi_choice | Checkbox options | List of selected strings |
free_text | Text input field | Free-form string |
Smart Review Prompting
A two-step flow that routes happy users to the Play Store review dialog and unhappy users to a feedback form:
- In-app question: “Do you enjoy using [app]?”
- Positive response — shows the Google In-App Review API dialog
- Negative response — shows a free-text feedback form
AppDNA.surveys.promptReview(activity)
Smart review prompting is rate-limited: maximum 3 times per year, at least 90 days apart. The SDK enforces these limits automatically.
SurveyDelegate
AppDNA.surveys.delegate = object : SurveyDelegate {
override fun onSurveyPresented(surveyId: String) {
println("Survey shown: $surveyId")
}
override fun onSurveyResponseSubmitted(surveyId: String, responses: List<SurveyAnswer>) {
for (answer in responses) {
when (answer.questionType) {
"nps" -> {
val score = answer.numericValue ?: 0
if (score >= 9) showReferralPrompt()
else if (score <= 6) showSupportLink()
}
"free_text" -> {
answer.stringValue?.let { println("Feedback: $it") }
}
}
}
}
override fun onSurveyDismissed(surveyId: String) {
println("Survey dismissed: $surveyId")
}
}
SurveyAnswer
| Property | Type | Description |
|---|
questionId | String | Identifier for the question |
questionType | String | Question type (e.g., "nps", "free_text") |
stringValue | String? | Text response |
numericValue | Int? | Numeric response |
arrayValue | List<String>? | Multi-select response |
boolValue | Boolean? | Boolean response |
Survey questions support rich media content configured in the Console:
- Header images — add images above survey questions
- Icons in options — use icon references in choice options (Lucide, Material, or emoji)
- Thank-you animations — Lottie or confetti effects on survey completion
- Haptic feedback — triggered on option selection and submission
See the Rich Media guide for details on supported formats.
Auto-Tracked Events
| Event | Trigger |
|---|
survey_presented | A survey is displayed |
survey_response_submitted | User submits a survey response |
survey_dismissed | Survey is closed without completing |
Full Example
import ai.appdna.sdk.AppDNA
import ai.appdna.sdk.SurveyDelegate
import ai.appdna.sdk.SurveyAnswer
class FeedbackManager(private val activity: Activity) : SurveyDelegate {
init {
AppDNA.surveys.delegate = this
}
fun askForFeedback() {
AppDNA.surveys.present("nps_q1_2026", activity)
}
fun promptForReview() {
AppDNA.surveys.promptReview(activity)
}
override fun onSurveyPresented(surveyId: String) { }
override fun onSurveyResponseSubmitted(surveyId: String, responses: List<SurveyAnswer>) {
val nps = responses.firstOrNull { it.questionType == "nps" }
val score = nps?.numericValue ?: return
when {
score >= 9 -> showReferralPrompt()
score <= 6 -> showSupportLink()
}
}
override fun onSurveyDismissed(surveyId: String) { }
}
Surveys are created in the Console under Feedback > Surveys. Trigger-based surveys appear automatically — manual presentation with present() is for precise control over timing.