VoidhashDocs
DevelopersHelp centerGuidesAPI reference
Dashboard
Get started
Introduction
Quickstart
How Voidhash works
Glossary
Versions and releases
Concepts
Teams, projects and environments
API keys
Products and perks
Offers
Subscription lifecycle
People and identity
Groups
Events
Features
Paywalls in your app
Feature flags and A/B tests
Paywall components
Push notifications
Ad attribution in your app
Client SDKs
React Native
Swift
Kotlin
Web
Your backend
Node.js
PHP
Go
Rust
Other languages
Payment sources
Forward store notifications
Developer tools
CLI
MCP server
Query analytics with VoidQL
Kotlin SDK
Kotlin quickstart
Display a paywall
Make and restore purchases
Check access
Identify users
Evaluate feature flags
Capture analytics
Groups
Track screens
Development mode
Kotlin SDK reference
Errors
Diagnostic codes
Troubleshooting

Kotlin SDK

Requirements, installation and client setup of the Voidhash Android SDK.

The Kotlin SDK adds purchases, access checks, paywalls, feature flags and analytics to native Android apps that sell through Google Play. Start with the quickstart. Options and methods are in the Kotlin SDK reference.

Compatibility

  • minSdk 23 and compileSdk 35. Play Billing 9 requires compileSdk 35 of your app too.
  • Android Gradle Plugin 8.9.0, Kotlin 2.0.21, Java 8 bytecode.
  • Play Billing 9.1.0, which the SDK brings in.

Install

The Kotlin SDK installs from a clone of the Voidhash repository, as a Gradle included build:

git clone https://github.com/voidhashcom/voidhash.git third_party/voidhash
settings.gradle.kts
includeBuild("third_party/voidhash/libraries/android")
app/build.gradle.kts
dependencies {
    implementation("com.voidhash:sdk")
}

The quickstart lists the rest of the setup.

To pin a build, check out a commit in the clone (git -C third_party/voidhash checkout <commit>), or add the repository as a Git submodule so your repository records the commit.

Alpha

The SDK is an alpha release and isn't on Maven Central yet. Pin a commit and update regularly. See Versions and releases.

The SDK is three Gradle modules in libraries/android of the Voidhash repository:

ModuleCoordinateContents
sdkcom.voidhash:sdkThe SDK you call: com.voidhash.sdk.
corecom.voidhash:coreThe shared engine sdk depends on. You don't add it yourself.
uicom.voidhash:uiThe optional paywall renderer, with a native library for arm64-v8a and x86_64.

Add the paywall renderer builds the ui module's native library. To vendor the sources instead, copy core, sdk and ui into your project, include them, and name their paths in gradle.properties. Modules included as :core and :sdk need no properties.

settings.gradle.kts
include(":voidhash-core", ":voidhash-sdk", ":voidhash-ui")
project(":voidhash-core").projectDir = file("third_party/voidhash/core")
project(":voidhash-sdk").projectDir = file("third_party/voidhash/sdk")
project(":voidhash-ui").projectDir = file("third_party/voidhash/ui")
gradle.properties
voidhash.coreProjectPath=:voidhash-core
voidhash.sdkProjectPath=:voidhash-sdk

Then depend on project(":voidhash-sdk") and project(":voidhash-ui").

Data collection

Account for what the SDK sends when you fill in Google Play's Data safety form.

DataWhen the SDK collects itUsed for
Device IDAlways. The SDK creates an anonymous ID for each install.App functionality, analytics
User IDWhen you call identify with your user ID.App functionality, analytics
Email address and nameOnly when you pass them to identify or set them as attributes.App functionality
Purchase historyAlways. The SDK reports the Google Play purchases it observes.App functionality, analytics
Product interactionWhile analytics consent is granted, the default: captured events, lifecycle events and screen views.Analytics
Advertising dataWhile consent is granted: the campaign, ad and click IDs behind the install, from Google Play or Meta's apps, unless installAttribution = false.Analytics
Crash dataWhile consent is granted: that the app crashed, and in which version. No crash logs.Analytics
Coarse locationWhile consent is granted. Voidhash derives a city-level location from the request's IP address.Analytics

Events also carry the app name, version and build, package name, device brand and model, OS version, locale and time zone. The SDK reads no advertising ID or Android ID, and shares no data for tracking.

The SDK stores its state in the com.voidhash.sdk.cache shared preferences file and in files/voidhash/. Android Auto Backup includes both, so a restored device keeps the anonymous ID. To start restored installs with a new anonymous ID, exclude com.voidhash.sdk.cache.xml (domain sharedpref) in your backup rules. Keep files/voidhash/, which holds queued purchases. An anonymous buyer on a restored device then needs restorePurchases() to regain access.

Create the client

Configure one client when your app starts, then initialize it:

App.kt
val voidhash = Voidhash.configure(
    context = this,
    publishableKey = "vh_pk_...",
    options = VoidhashOptions(debug = BuildConfig.DEBUG),
)

ProcessLifecycleOwner.get().lifecycleScope.launch {
    voidhash.initialize()
}

Ship the publishable key, never a secret key. Release builds, including Play testing tracks, use the production key; debug builds in development mode use the test key. See API keys.

configure is synchronous and returns the client, also available as Voidhash.shared. Until initialize() runs, the client does no work, so you can defer it until consent or sign-in. initialize() loads stored state, then connects to Google Play, loads your project's configuration and resumes unfinished purchases in the background. It never fails because Voidhash is unreachable, and repeated calls are safe. Until the configuration has loaded, purchase throws CONFIGURATION_MISSING.

Voidhash runs only in your app's main process. In another process, such as a service with its own android:process, configure returns an inert client. If your UI runs in a named process, set runInSecondaryProcess = true and configure Voidhash in that process only.

If your app has a directBootAware component, Android can start it after a restart before the device is first unlocked. The app's storage isn't available then, so the client waits for the unlock. It keeps the newest 100 events and screens and up to 100 identify and attribute calls in memory, always keeps a reported purchase, and answers reads with no value, marked stale and expired. getDistinctId() and sessionId return temporary IDs until the unlock; don't store or send them. purchase, restorePurchases() and syncPurchases() throw DEVICE_LOCKED, and presentFlow returns FLOW_UNAVAILABLE.

Once the device is unlocked, the SDK starts on its own and makes the held calls in order, and initialize() returns once the client works. The client configure returned keeps working, so you don't need to read Voidhash.shared again.

Calling configure again replaces the client: the previous one stops, and its queued data moves to the new one when both use the same key and baseUrl. The previous client's identity, attribute and store methods throw CLIENT_SHUT_DOWN. For every option VoidhashOptions takes, see Client options.

Next steps

  • Kotlin quickstart.
  • Kotlin SDK reference.
  • Display a paywall.
  • Troubleshooting.

Something wrong or missing on this page? Tell us by email or on Discord.

PreviousTroubleshootingNextKotlin quickstart

On this page

CompatibilityInstallData collectionCreate the clientNext steps