SDKs
Linkzly provides SDKs to make it easier to send gaming events from your app or game. The SDKs handle event batching, retry logic, session management, and HMAC r
SDKs
Linkzly provides SDKs to make it easier to send gaming events from your app or game. The SDKs handle event batching, retry logic, session management, and HMAC request signing so you do not have to build that infrastructure yourself.
When to use the SDK: Use the SDK when you are integrating from inside a game or app. The SDK batches events automatically, manages sessions in the background, and queues events during network outages.
When to use the REST API directly: Use the REST API when you are integrating from a server, a script, or a platform where no SDK is available. You construct and send HTTP requests manually.
17.1 Android SDK (Kotlin)
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 requires a minimum Android API level as documented in the release notes for each version.
Finding Your SDK Key
Your SDK key is available in the Linkzly console under Game Settings โ SDK Configuration โ SDK Key. Navigate to /dashboard/gaming/[gameId]/settings to find it. The SDK key is masked by default; use the eye icon to reveal it or the copy button to copy it to your clipboard.
Initialization
Call LinkzlyGamingTracking.configure() once, early in your application lifecycle (for example, in Application.onCreate()):
LinkzlyGamingTracking.configure(context, GamingOptions(
apiKey = "YOUR_SDK_KEY",
organizationId = "YOUR_ORG_ID",
gameId = "YOUR_GAME_ID"
))
Your SDK key, organization ID, and game ID are all available from the Gaming Intelligence settings page in the Linkzly console.
Configuration Options
| Parameter | Type | Default | Description |
|---|---|---|---|
apiKey |
String | required | Your game's SDK key from the Linkzly console |
organizationId |
String | required | Your organization ID |
gameId |
String | required | Your game ID |
baseUrl |
String | https://gaming.linkzly.com |
API base URL; override only if using a custom deployment |
sdkVersion |
String | auto | SDK version string; set automatically |
gameVersion |
String | required | Your game's version string (e.g., 2.1.0) |
maxBatchSize |
Int | 100 |
Maximum events per batch. Range: 1โ100 |
maxBatchBytes |
Int | 524288 |
Maximum batch payload size in bytes. Range: 1,024โ524,288 (512 KB) |
flushIntervalMs |
Long | 5000 |
How often the SDK sends queued events, in milliseconds. Range: 500โ5,000 |
maxRetries |
Int | 3 |
Retry attempts on a failed send. Range: 0โ3 |
retryDelayMs |
Long | 1000 |
Delay between retry attempts in milliseconds. Range: 250โ1,000 |
maxQueueSize |
Int | 10000 |
Maximum events held in the queue before overflow. Range: 10โ10,000 |
sessionTimeoutMs |
Long | 1800000 |
Inactivity duration before a session ends. Minimum: 30 minutes (1,800,000 ms) |
autoSessionTracking |
Boolean | true |
Automatically send session_start and session_end events |
signingSecret |
String? | null |
HMAC signing secret; required if HMAC Signing is enabled in Game Settings |
debug |
Boolean | false |
Enable verbose debug logging in Logcat |
includeTraits |
Boolean | false |
Attach player traits to every event payload |
Core Methods
| Method | Description |
|---|---|
identify(playerId, traits?) |
Set the current player's identity. Pass optional key-value traits (age range, segment, etc.). Call this after login. |
track(eventType, data?, immediateFlush?) |
Track a custom event. data is an optional key-value object. Set immediateFlush = true to send without waiting for the next batch. |
startSession() |
Manually start a new session. Only needed if autoSessionTracking = false. |
endSession() |
Manually end the current session. Only needed if autoSessionTracking = false. |
setAttribution(clickId?, deferredDeepLink?, metadata?) |
Set attribution data for the current player, used to tie installs back to campaigns. |
clearAttribution() |
Remove stored attribution data. |
flush(reason, callback?) |
Force-send all queued events immediately. Useful before the app closes. |
reset() |
Tear down the SDK and clear the player identity and event queue. Call on logout. |
getPendingCount() |
Returns the number of events currently waiting in the queue. |
hasInflightBatch() |
Returns true if a batch is currently in transit to the server. |
Automatic Session Tracking
When autoSessionTracking = true (the default), the SDK manages sessions for you:
- A
session_startevent is sent automatically 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 receives a unique UUID as its
session_id.
You do not need to call startSession() or endSession() manually unless you have set autoSessionTracking = false.
The SDK also flushes queued events automatically based on the following triggers: BATCH_FULL, TIME_INTERVAL, SESSION_END, APP_BACKGROUND, MANUAL_FLUSH, QUEUE_OVERFLOW, ERROR_RECOVERY, and STARTUP_RECOVERY.
Event Types
The following event types are natively understood by the ingestion pipeline. Field values are case-insensitive.
Core events:
| Event Type | Description |
|---|---|
session_start |
Session began. Sent automatically when autoSessionTracking = true. |
session_end |
Session ended. Sent automatically when autoSessionTracking = true. |
app_install |
First-time app installation detected. |
app_open |
App brought to the foreground. |
app_close |
App sent to the background or terminated. |
Gaming events:
| Event Type | Description |
|---|---|
level_start |
Player started a level. |
level_complete |
Player completed a level. |
achievement_unlocked |
Player unlocked an achievement. |
tutorial_complete |
Player completed the tutorial. |
currency_spent |
In-game currency was spent. |
currency_earned |
In-game currency was earned. |
item_purchased |
Player purchased an item. |
battle_won |
Player won a battle or match. |
tournament_join |
Player joined a tournament. |
friend_invited |
Player invited a friend. |
Monetization events:
| Event Type | Description |
|---|---|
purchase |
Real-money purchase completed. |
subscription_start |
Subscription started. |
subscription_renew |
Subscription renewed. |
subscription_cancel |
Subscription cancelled. |
ad_watched |
Player watched an ad. |
Custom events:
For events that do not match a built-in type, use event_type: 'custom' and include the specific event name inside the data object:
{
event_type: 'custom',
data: {
custom_event_name: 'boss_defeated',
boss_id: 'dragon-king',
difficulty: 'hard'
}
}
17.2 Web / JavaScript SDK
For web games and browser-based integrations, use the standard fetch API with the correct headers. There is no npm package to install โ use the fetch-based approach below directly.
Initialization
Set up a client object with the required headers:
const client = {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${SDK_KEY}`,
'X-Organization-ID': ORG_ID,
'X-Game-ID': GAME_ID,
}
};
Replace SDK_KEY, ORG_ID, and GAME_ID with the values from your game's settings page in the Linkzly console. Your SDK key is located at Game Settings โ SDK Configuration โ SDK Key (/dashboard/gaming/[gameId]/settings).
Sending Events
Post events to the ingestion endpoint:
await fetch('https://gaming.linkzly.com/api/v1/gaming/events', {
method: 'POST',
headers: client.headers,
body: JSON.stringify({
events: [{
event_id: crypto.randomUUID(),
event_type: 'session_start',
timestamp: new Date().toISOString(),
player_id: 'player-001',
session_id: 'session-uuid',
platform: 'web',
data: {},
}],
}),
});
The endpoint accepts an array of events in a single request. For the full list of event object fields, see Section 8.9 of the documentation.
Response Format
The ingestion endpoint returns 202 Accepted with a summary:
{
"success": true,
"batch_id": "...",
"events_received": 5,
"events_valid": 5,
"events_dropped": 0,
"trace_id": "...",
"server_timestamp": "2025-01-01T00:00:00Z"
}
batch_idโ Unique identifier for this batchevents_receivedโ Total events in the payloadevents_validโ Events that passed validationevents_droppedโ Events that failed validation (usetrace_idfor debugging)
Batch Validation Limits
The ingestion endpoint enforces the following limits per request:
| Limit | Value |
|---|---|
| Maximum events per batch | 1,000 |
| Maximum size per event | 10 KB |
| Maximum request body size | 1 MB |
| Maximum events per player per batch | 100 |
Maximum data object nesting depth |
3 levels |
| Maximum timestamp age | 24 hours in the past |
| Maximum timestamp future skew | 5 minutes in the future |
Events that exceed these limits are dropped and counted in events_dropped. The rest of the batch is still processed.
Rate Limiting
The ingestion endpoint allows 1,000 requests per minute with a burst allowance of 200 requests. Requests exceeding the rate limit receive 429 Too Many Requests.
17.3 Unity SDK (C#)
For Unity games, use UnityWebRequest to send events to the ingestion endpoint.
Sending Events via UnityWebRequest
IEnumerator SendEvent() {
var payload = JsonUtility.ToJson(new { events = new[] { /* event object */ } });
var request = new UnityWebRequest(BASE_URL + "/api/v1/gaming/events", "POST");
request.SetRequestHeader("Authorization", "Bearer " + SDK_KEY);
request.SetRequestHeader("X-Organization-ID", ORG_ID);
request.SetRequestHeader("X-Game-ID", GAME_ID);
request.SetRequestHeader("Content-Type", "application/json");
request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(payload));
request.downloadHandler = new DownloadHandlerBuffer();
yield return request.SendWebRequest();
}
Set BASE_URL to https://gaming.linkzly.com and replace SDK_KEY, ORG_ID, and GAME_ID with your game's credentials from the Linkzly console (available at Game Settings โ SDK Configuration โ SDK Key).
Use JsonUtility.ToJson to serialize your event object before sending. The request is a coroutine โ call it using StartCoroutine(SendEvent()) from your MonoBehaviour.
17.4 React Native SDK
The React Native SDK supports both iOS and Android from a single codebase.
Initialize the SDK with your apiKey, organizationId, and gameId. The SDK automatically detects the platform and sets it to ios or android based on the device.
The React Native SDK exposes the same core methods as the Android SDK:
identify(playerId, traits?)track(eventType, data?, immediateFlush?)startSession()endSession()
Refer to the Android SDK section for method descriptions. Initialization parameters and behavior are identical.
17.5 Supported Platforms
| Platform | SDK / Language |
|---|---|
| Android | Kotlin / Java |
| iOS | Swift / Objective-C / React Native |
| Web | JavaScript / TypeScript |
| Unity | C# (UnityWebRequest) |
| Unreal Engine | C++ / Blueprint |
| Steam | C++ / Steamworks SDK |
| PlayStation | C++ (PS SDK integration) |
| Xbox | C++ (GDK integration) |
| Nintendo Switch | C++ (NX SDK) |
| Epic Games Store | C++ (EOS SDK) |
| Discord Activities | JavaScript |
For platforms not listed above, use the REST API directly by sending HTTP POST requests to the ingestion endpoint with the required headers.
17.6 HMAC Request Signing
HMAC signing adds an extra layer of security by letting the Linkzly ingestion API verify that each request actually came from your game and was not tampered with in transit.
HMAC Signing is enabled by default for all new games. You can disable it in Game Settings โ Security Settings โ HMAC Signing Required if you need to temporarily send unsigned requests (not recommended for production).
When to Use It
Use HMAC signing whenever your game sends events from a client (browser, mobile app, game client). It prevents third parties from injecting fake events into your analytics by forging requests.
Required Headers
When HMAC signing is enabled, every ingestion request must include three additional headers in addition to the standard authentication headers:
Standard authentication headers (always required):
| Header | Description |
|---|---|
Authorization |
Bearer <SDK_KEY> โ your game's SDK key |
X-Organization-ID |
Your organization ID |
X-Game-ID |
Your game ID |
HMAC signing headers (required when HMAC is enabled):
| Header | Description |
|---|---|
X-Signature-256 |
The HMAC-SHA256 signature of the request, hex-encoded |
X-Timestamp |
The current Unix timestamp in milliseconds |
X-Nonce |
A UUID v4 one-time nonce to prevent replay attacks |
How the Signature Is Computed
Concatenate the timestamp, nonce, and raw request body with dots, then compute an HMAC-SHA256 hash using your signing secret:
HMAC-SHA256(signingSecret, "{timestamp}.{nonce}.{bodyString}")
The result is hex-encoded and sent as the X-Signature-256 header value.
Your signing secret is available in Game Settings โ SDK Configuration. Treat it like a password โ do not include it in client-side code that is publicly visible.
Replay Protection
Linkzly checks the X-Timestamp value against the server's current time. Requests with a timestamp outside the replay window are rejected with 401 Unauthorized.
The replay window is fixed at 300 seconds (5 minutes). This value is not configurable.
Each nonce is stored for 60 seconds after it is first seen. If the same nonce is submitted again within that window, the request is rejected with 401 Unauthorized. This ensures that even a legitimately signed request cannot be replayed successfully within the replay window.
To avoid rejection:
- Generate a new UUID v4 nonce for every request.
- Ensure your device clock is reasonably synchronized (within 5 minutes of UTC).
Was this helpful?
Help us improve our documentation