Skip to main content
You control when and where to present studies inside your app.

Basic usage

Present a study by its ID. You can find the ID in the Integration tab of your study settings.
import so.pillow.sdk.PillowStudy

PillowSDK.presentStudy(
    activity = this,
    study = PillowStudy(id = "your-study-id-here")
)
You must pass the current Activity so the SDK can present the modal.

Session resumption

By default, the SDK remembers where a user left off. If a user partially completes a study and you call presentStudy() again with the same ID, they resume from where they stopped.

Start a fresh session

To force a new conversation instead of resuming, pass presentation options with forceFreshSession set to true:
PillowSDK.presentStudy(
    activity = this,
    study = PillowStudy(id = "your-study-id-here"),
    options = PillowStudyPresentationOptions(forceFreshSession = true)
)
This clears the stored session for that study and starts a new conversation from the beginning.

Skip repeat exposure

To ask the backend to no-op when the current SDK user was already exposed to the same study, pass presentation options:
import so.pillow.sdk.PillowStudyPresentationOptions

PillowSDK.presentStudy(
    activity = this,
    study = PillowStudy(id = "your-study-id-here"),
    options = PillowStudyPresentationOptions(skipIfAlreadyExposed = true)
)
skipIfAlreadyExposed defaults to false, so existing calls keep presenting unless you opt in.

Monitor lifecycle

Pass a listener to presentStudy() to receive lifecycle callbacks:
import android.util.Log
import so.pillow.sdk.PillowSDK
import so.pillow.sdk.PillowStudy
import so.pillow.sdk.PillowStudyListener

PillowSDK.presentStudy(
    activity = this,
    study = PillowStudy(id = "your-study-id-here"),
    options = PillowStudyPresentationOptions(),
    listener = object : PillowStudyListener {
        override fun studyDidPresent(study: PillowStudy) {
            // study modal appeared on screen
        }
        override fun studyDidSkip(study: PillowStudy) {
            // study was intentionally skipped and not presented
        }
        override fun studyDidFinish(study: PillowStudy) {
            // user finished or dismissed the study
        }
        override fun studyDidFailToLoad(study: PillowStudy, error: Throwable) {
            Log.e("Pillow", "Study failed to load: ${error.message}")
        }
    },
)
Passing an anonymous object at the call site is the standard Android pattern here. Unlike SwiftUI, you do not need a separate retained coordinator.
MethodDescription
studyDidPresent(study)The study modal appeared on screen
studyDidSkip(study)The study was intentionally skipped and not presented
studyDidFinish(study)The user finished or dismissed the study
studyDidFailToLoad(study, error)The study could not be loaded or presented. Access error for the cause — for example, a network error, the Activity was destroyed, or another study is already being shown
All listener methods are invoked on the main thread, so you can safely update your UI from them. In Kotlin, all methods have no-op defaults — override only the ones you need.
Use the listener to track study engagement, trigger follow-up actions after a conversation, or show a fallback if the study fails to load.

Best practices

  • Call setExternalId() before presenting so the study is linked to the right user
  • Copy the study ID from the Integration tab in your study settings
  • Test in test mode first — test conversations don’t appear in your dashboard data
Make sure the study is set to live mode before presenting it to real users. Studies in test mode work for testing but data won’t appear in your production dashboard.