Android SDK Setup Guide (Kotlin)
---
sidebar_label: Android SDK (Kotlin) sidebar_category: Gaming SDK Guides
Android SDK Setup Guide (Kotlin)
This guide covers integrating Gaming Intelligence into an Android app using Kotlin. The Android SDK handles event batching, retry logic, session lifecycle management, and HMAC request signing automatically, so you can focus on tracking the events that matter to your game.
Prerequisites
Before you begin, have the following ready:
| Requirement | Where to Find It |
|---|---|
| SDK Key | Linkzly Console → Gaming → [Your Game] → Settings → SDK Configuration → SDK Key |
| Organization ID | Linkzly Console → Organization Settings |
| Game ID | Linkzly Console → Gaming → [Your Game] → Settings → General Settings |
| Game Version | Your app's versionName from build.gradle |
| Minimum Android API Level | See the Linkzly SDK release notes for the version you are installing |
Your SDK key is masked by default in the console. Use the eye icon to reveal it or the copy button to copy it directly.
Installation
Add the Linkzly Gaming dependency to your app-level build.gradle file:
dependencies {
implementation 'com.linkzly:gaming-tracking:LATEST_VERSION'
}
Replace LATEST_VERSION with the version listed in the Linkzly SDK release notes. The SDK is distributed via Maven Central — no additional repository configuration is required for most projects.
Sync your project after adding the dependency.
Initialization
Call LinkzlyGamingTracking.configure() once, as early as possible in your app's lifecycle. The recommended location is Application.onCreate():
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
LinkzlyGamingTracking.configure(
context = this,
options = GamingOptions(
apiKey = "YOUR_SDK_KEY",
organizationId = "YOUR_ORG_ID",
gameId = "YOUR_GAME_ID",
gameVersion = BuildConfig.VERSION_NAME,
signingSecret = "YOUR_SIGNING_SECRET", // Required if HMAC is enabled
debug = BuildConfig.DEBUG
)
)
}
}
Register your custom Application class in AndroidManifest.xml:
<application
android:name=".MyApplication"
...>
Note: Call
configure()only once. Calling it again after initialization has no effect. If you need to re-initialize with different credentials, callreset()first.
Configuration Options
The GamingOptions class accepts the following parameters:
| Parameter | Type | Default | Required | Description |
|---|---|---|---|---|
apiKey |
String |
— | Yes | Your game's SDK key from the Linkzly console |
organizationId |
String |
— | Yes | Your organization ID |
gameId |
String |
— | Yes | Your game ID |
gameVersion |
String |
— | Yes | Your game's version string (e.g., 2.1.0) |
baseUrl |
String |
https://gaming.linkzly.com |
No | API base URL. Override only if using a custom deployment |
maxBatchSize |
Int |
100 |
No | Maximum number of events per outgoing batch. Range: 1–100 |
maxBatchBytes |
Int |
524288 |
No | Maximum payload size per batch in bytes. Range: 1,024–524,288 (512 KB) |
flushIntervalMs |
Long |
5000 |
No | How often the SDK sends queued events, in milliseconds. Range: 500–5,000 |
maxRetries |
Int |
3 |
No | Number of retry attempts after a failed send. Range: 0–3 |
sessionTimeoutMs |
Long |
1800000 |
No | Inactivity duration before a session is automatically ended. Minimum: 30 minutes (1,800,000 ms) |
autoSessionTracking |
Boolean |
true |
No | When true, the SDK automatically emits session_start and session_end events based on app foreground/background transitions |
signingSecret |
String? |
null |
Conditionally | HMAC signing secret. Required when HMAC Signing is enabled in Game Settings |
debug |
Boolean |
false |
No | Enables verbose logging in Logcat under the LinkzlyGaming tag |
Core Methods
Once initialized, interact with the SDK through the LinkzlyGamingTracking singleton:
| Method | Signature | Description |
|---|---|---|
identify |
identify(playerId: String, traits: Map<String, Any>? = null) |
Set the current player's identity. Call after login. Pass optional key-value traits (age range, segment, VIP status, etc.). |
track |
track(eventType: String, data: Map<String, Any>? = null, immediateFlush: Boolean = false) |
Track a game event. data is an optional key-value map. Set immediateFlush = true to bypass the batch queue and send immediately. |
startSession |
startSession() |
Manually start a new session. Only needed when autoSessionTracking = false. |
endSession |
endSession() |
Manually end the current session. Only needed when autoSessionTracking = false. |
setAttribution |
setAttribution(clickId: String? = null, deferredDeepLink: String? = null, metadata: Map<String, Any>? = null) |
Store attribution data for the current player (ties installs back to campaigns). |
clearAttribution |
clearAttribution() |
Remove stored attribution data. |
flush |
flush(reason: String = "MANUAL", callback: ((success: Boolean) -> Unit)? = null) |
Force-send all queued events immediately. Call before the app terminates. |
reset |
reset() |
Clear the player identity, flush the event queue, and tear down the SDK instance. Call on logout. |
getPendingCount |
getPendingCount(): Int |
Returns the number of events currently waiting in the local queue. |
hasInflightBatch |
hasInflightBatch(): Boolean |
Returns true if a batch is currently in transit to the server. |
Tracking Events
Identifying the Player
Call identify() after the player logs in to associate all subsequent events with their ID:
LinkzlyGamingTracking.identify(
playerId = "player-001",
traits = mapOf(
"age_range" to "18-24",
"segment" to "whale",
"country" to "US"
)
)
Tracking a Game Event
Use track() to send any event. Provide the event type and an optional data map:
// Level completed
LinkzlyGamingTracking.track(
eventType = "level_complete",
data = mapOf(
"level_id" to "level-5",
"score" to 12400,
"duration_seconds" to 182,
"stars" to 3
)
)
// In-app purchase
LinkzlyGamingTracking.track(
eventType = "purchase",
data = mapOf(
"item_id" to "gem-pack-500",
"amount" to 4.99,
"currency" to "USD",
"store" to "google_play"
)
)
// Custom event
LinkzlyGamingTracking.track(
eventType = "custom",
data = mapOf(
"custom_event_name" to "boss_defeated",
"boss_id" to "dragon-king",
"difficulty" to "hard",
"attempts" to 3
)
)
For a complete list of supported event types, see Section 17.1 of the SDKs documentation.
Sending Immediately
For high-priority events (purchases, installs), pass immediateFlush = true to skip the batch queue:
LinkzlyGamingTracking.track(
eventType = "purchase",
data = mapOf("amount" to 9.99, "currency" to "USD"),
immediateFlush = true
)
Session Handling
Automatic Session Tracking (Default)
When autoSessionTracking = true, the SDK manages sessions for you using Android's ActivityLifecycleCallbacks:
- A
session_startevent is sent when the app comes to the foreground. - A
session_endevent is sent when the app goes to the background, after the inactivity timeout defined bysessionTimeoutMs. - Each session is assigned a unique UUID as its
session_id.
You do not need to call startSession() or endSession() manually when using automatic tracking.
The SDK auto-flushes queued events on the following triggers: BATCH_FULL, TIME_INTERVAL, SESSION_END, APP_BACKGROUND, MANUAL_FLUSH, QUEUE_OVERFLOW, ERROR_RECOVERY, and STARTUP_RECOVERY.
Manual Session Tracking
To manage sessions yourself, set autoSessionTracking = false in GamingOptions and call the session methods explicitly:
// On game screen entered / login complete
LinkzlyGamingTracking.startSession()
// On game screen exited / logout / app paused
LinkzlyGamingTracking.endSession()
Flushing Before App Exit
Call flush() before the app terminates to ensure no queued events are lost:
override fun onStop() {
super.onStop()
LinkzlyGamingTracking.flush(reason = "APP_BACKGROUND")
}
HMAC Request Signing
HMAC signing is enabled by default for all new games. When enabled, every request must include a valid signature computed from your signing secret.
Pass the signingSecret in GamingOptions — the SDK computes the signature and attaches the required headers (X-Signature-256, X-Timestamp, X-Nonce) automatically on every outgoing batch:
GamingOptions(
apiKey = "YOUR_SDK_KEY",
organizationId = "YOUR_ORG_ID",
gameId = "YOUR_GAME_ID",
gameVersion = "1.0.0",
signingSecret = "YOUR_SIGNING_SECRET"
)
Your signing secret is available in the console under Game Settings → SDK Configuration. It is separate from the SDK Key.
Security note: Do not hardcode your signing secret in version-controlled source files. Use a build-time injection mechanism (Gradle secrets, environment variables, or a secure secrets manager) to supply it at build time.
To disable HMAC for development or testing, toggle off HMAC Signing Required in Game Settings → Security Settings and omit signingSecret from GamingOptions. Re-enable it before releasing to production.
ProGuard and R8
The SDK ships with its own embedded ProGuard consumer rules. No manual ProGuard configuration is required — the rules are applied automatically when you build a release APK or AAB.
Troubleshooting
Events are not appearing in the Linkzly dashboard
- Set
debug = trueinGamingOptionsand check Logcat for theLinkzlyGamingtag. - Verify your
apiKey,organizationId, andgameIdare correct (no trailing spaces). - Confirm
LinkzlyGamingTracking.configure()is called before anytrack()calls. - Check your internet connectivity and confirm the device can reach
https://gaming.linkzly.com.
HMAC errors (401 Unauthorized)
- Confirm
signingSecretinGamingOptionsmatches the secret shown in Game Settings → SDK Configuration. - Verify the device clock is synchronized with UTC. The replay window is fixed at 300 seconds — requests with timestamps more than 5 minutes out of sync are rejected.
- Check the Logcat output for
HMAC_SIGNING_FAILEDentries.
High events_dropped count in batch responses
Events may be dropped if they exceed the 10 KB per-event limit, have a timestamp older than 24 hours, or have a future timestamp more than 5 minutes ahead. Enable debug = true to log dropped events with their reasons.
Crash on configure() call
Ensure you are passing an Application context, not an Activity context. The SDK registers lifecycle callbacks on the Application and will throw if given an Activity.
Was this helpful?
Help us improve our documentation