Overview
One SDK context for every service
Kiwi SDK is a developer preview for Unity projects. A single KiwiSdkConfig selects the SDK app and environment used by accounts, remote config, cloud saves, analytics, diagnostics, and tenant-scoped realtime services.
dev is separate from staging and production. The app ID and environment travel with each SDK request.Recommended integration path
- 1Create an SDK app
Open the SDK Portal, create an app, and copy its public app ID.
- 2Install the tagged package
Add the Kiwi SDK v0.3.0 Git URL and its two direct Git dependencies to the Unity project manifest.
- 3Start in dev
Initialize against
dev, authenticate a test account, and verify each service independently. - 4Promote deliberately
Publish config and validate data separately in staging before selecting production.
Getting started
Requirements and package access
The current package is com.kiwistudios.sdk version 0.3.0 and targets Unity 2022.3 or newer. The tagged developer-preview package is available from GitHub.
kiwi-sdk-v0.3.0 rather than an unversioned branch so package updates remain deliberate and reproducible.Required packages
| Package | Purpose | Declared version |
|---|---|---|
com.cysharp.unitask | Async Unity operations | 2.5.10 |
com.itisnajim.socketiounity | Realtime Socket.IO transport | 1.1.5 |
com.unity.nuget.newtonsoft-json | JSON serialization | 3.2.2 |
com.unity.modules.unitywebrequest | HTTP transport | 1.0.0 |
The dependencies are declared in the Kiwi package manifest. Unity cannot resolve transitive Git dependencies, so Git installs must also add UniTask and SocketIOUnity directly to the project manifest. Registry consumers may configure the com.cysharp and com.itisnajim OpenUPM scopes instead.
Install the tagged Git package
https://github.com/ArtemisMoysen/GDW4.git?path=Packages/com.kiwistudios.sdk#kiwi-sdk-v0.3.0
In Packages/manifest.json, add the tagged Kiwi SDK URL plus UniTask 2.5.10 and SocketIOUnity 1.1.5 as direct dependencies.
Or add the package from disk
- Put the approved
com.kiwistudios.sdkfolder in a stable local location, normally your project'sPackagesdirectory. - In Unity, open Window → Package Manager.
- Select +, then Add package from disk….
- Select
com.kiwistudios.sdk/package.json.
Packages/com.kiwistudios.sdk/package.json
Getting started
Create an app in the SDK Portal
Sign in to the SDK Portal and create one SDK app for the game. The generated ID starts with ksa_ and does not change.
Environments
Every app begins with dev, staging, and production. Remote config, cloud saves, analytics, achievements, crash data, presence, lobbies, queues, and matches are scoped by the selected app and environment. Friend relationships remain account-global.
Getting started
Initialize once at application boot
Create KiwiSdkConfig before calling any service client. The service origin is owned by the package; the public constructor accepts the app ID, environment, request timeouts, and optional session cache keys.
using KiwiStudios.Sdk;
using UnityEngine;
public sealed class KiwiSdkBootstrap : MonoBehaviour
{
private void Awake()
{
if (KiwiSdk.IsInitialized) { return; }
var _config = new KiwiSdkConfig(
"ksa_replace_with_your_app_id",
"dev",
20,
10);
KiwiSdk.Initialize(_config);
}
private void OnDestroy()
{
KiwiSdk.Shutdown();
}
}
Accounts
Authenticate, cache, then resolve the SDK session
Interactive sign-in uses LoginStep1 followed by LoginStep2. After a successful second step, call SetAuthenticated, CacheSession, and ResolveSdkSession. Resolution verifies that the signed-in account can establish the configured app-and-environment context.
RestoreSession validates cached credentials online and stores a rotated token after success. Offline identity is opt-in: it can restore a recent locally cached identity after a network failure, but only from native secure storage and never for cloud writes, realtime, or other online services.
using Cysharp.Threading.Tasks;
using KiwiStudios.Sdk;
using System.Threading;
public sealed class KiwiSessionService
{
public async UniTask<KiwiResult<KiwiAuthResponse>> Restore(CancellationToken _ct)
{
KiwiResult<KiwiAuthResponse> _session = await KiwiSdk.Auth.RestoreSession(_ct);
if (!_session.Result || !KiwiSdk.Auth.CanAccessOnlineServices)
{
return _session;
}
KiwiResult<KiwiSdkSessionResolveResponse> _resolved =
await KiwiSdk.Auth.ResolveSdkSession(_session.Data.UserId, _session.Data.Token, _ct);
if (!_resolved.Result)
{
return KiwiResult<KiwiAuthResponse>.Failure(_resolved.Error, _resolved.StatusCode);
}
return _session;
}
}
KiwiSecureSessionStore for macOS Keychain, iOS Keychain, Android Keystore, or Windows Credential Manager. Then opt in with _allowOfflineIdentity: true, set a short maximum age, and gate network features on CanAccessOnlineServices. PlayerPrefs is never accepted for offline identity.Core services
Load cached values before fetching updates
Publish a JSON object from the SDK Portal. At runtime, load the encrypted local cache first, then fetch the selected environment. A successful changed response is written to the cache before its hash is committed.
using Cysharp.Threading.Tasks;
using KiwiStudios.Sdk;
using Newtonsoft.Json.Linq;
using UnityEngine;
public sealed class GameConfigLoader
{
public async UniTask Load()
{
KiwiSdk.RemoteConfig.TryLoadCache();
KiwiResult<JObject> _result = await KiwiSdk.RemoteConfig.Fetch();
if (!_result.Result)
{
Debug.LogWarning(_result.Error);
return;
}
int _dailyMatchLimit = KiwiSdk.RemoteConfig.GetInt("dailyMatchLimit", 5);
Debug.Log(_dailyMatchLimit);
}
}
Remote config is delivered to player clients. Do not publish match keys, credentials, private endpoints, or values that must remain secret.
Core services
Choose JSON data or file slots
Cloud save provides two surfaces. User data is a small JSON record for profile-shaped values. Slots are immutable file commits for larger save payloads, with a manifest and SHA-256 integrity metadata.
User data protection labels
The signed-in player can write it. Use it for data that is safe to show outside the owner account.
The signed-in player can currently write it. Treat it as owner-scoped, client-authoritative data.
Normal player writes cannot change it, but the owning player receives it when loading. It is not a secret store.
Version and pass it to SaveUserDataVersioned; stale writes return 409 cloud_save_version_conflict.using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using KiwiStudios.Sdk;
public sealed class PlayerProfileSave
{
public async UniTask<KiwiResult<KiwiCloudSaveLoadResponse>> SaveAndLoad(
string _userId,
string _token)
{
var _publicData = new Dictionary<string, object>
{
["selectedTitle"] = "pathfinder"
};
var _protectedData = new Dictionary<string, object>
{
["tutorialComplete"] = true
};
KiwiResponse _save = await KiwiSdk.CloudSave.SaveUserData(
_userId,
_token,
_publicData,
_protectedData);
if (!_save.Result)
{
return KiwiResult<KiwiCloudSaveLoadResponse>.Failure(_save.Message, _save.StatusCode);
}
return await KiwiSdk.CloudSave.LoadUserData(_userId, _token);
}
}
File slots
Slot uploads commit a complete version atomically. Files are sent as base64 inside one JSON request; uploads and downloads are not streamed. Set ExpectedVersion to 0 when creating a slot or to the last observed version when replacing it. A stale upload or delete returns 409 cloud_save_version_conflict without changing the committed slot.
using System;
using System.Collections.Generic;
using System.Text;
using Cysharp.Threading.Tasks;
using KiwiStudios.Sdk;
public sealed class WorldSlotSave
{
public async UniTask<KiwiResult<KiwiCloudSaveSlotCommitResponse>> Upload(
string _userId,
string _token,
string _json,
long _lastKnownVersion)
{
var _request = new KiwiCloudSaveUploadRequest
{
SlotId = "autosave",
DisplayName = "Autosave",
ExpectedVersion = _lastKnownVersion,
Manifest = new Dictionary<string, object> { ["scene"] = "village" },
Files = new List<KiwiCloudSaveFileUploadData>
{
new KiwiCloudSaveFileUploadData
{
Path = "world/save.json",
ContentBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(_json))
}
}
};
return await KiwiSdk.CloudSave.UploadSlot(_userId, _token, _request);
}
}
Current default slot limits
| Boundary | Default |
|---|---|
| Slots per player, app, and environment | 10 |
| Files per slot | 32 |
| Decoded bytes per file | 512 KiB |
| Decoded bytes per slot | 700 KiB |
| Total slot bytes per player | 5 MiB |
| Serialized manifest | 64 KiB |
| Slot ID length | 96 characters |
| File path length | 191 characters |
Limits are service defaults and may change during the preview. Design save formats below every boundary because base64 and JSON add request overhead.
Core services
Track lightweight achievement progress
Use the portal to describe achievements, then call Unlock, Increment, or List from an authenticated player session.
KiwiResponse _response = await KiwiSdk.Achievements.Increment(
_userId,
_token,
"matches_played",
1);
Core services
Send intentional telemetry
Analytics can send immediately or persist a local queue. The queue holds up to 500 events and flushes at most 100 per request. The server returns an accepted count rather than per-event IDs; the client removes that many events from the front of the submitted batch. Failed and unaccepted events remain queued for retry.
Crash reports and logs are manual API calls. The package does not automatically subscribe to Unity log callbacks or unhandled exceptions.
var _payload = new Dictionary<string, object>
{
["mode"] = "practice",
["durationSeconds"] = 84
};
KiwiSdk.Analytics.QueueEvent("match_completed", _payload, "match.v1");
KiwiResponse _flush = await KiwiSdk.Analytics.FlushQueuedEvents(
_userId,
_token,
Application.version);
Realtime
First-party multiplayer foundations
The current realtime layer exposes low-level Socket.IO subscriptions plus convenience emitters for friends, presence, lobbies, and matchmaking. It is shaped by Kiwi Studios' first-party integration, not yet a general-purpose multiplayer contract.
- Subscribe to events before calling
ConnectAndIdentify. - Friends are account-global. Presence, lobbies, queue state, and matches are scoped to the configured app and environment.
- Multiple handlers may subscribe to the same event. Use
Off(eventName, handler)to remove one orOff(eventName)to remove all. ConnectAndIdentifyreturns aKiwiResponse, accepts cancellation, and uses the configured authentication timeout.- Reconnects emit state callbacks and automatically re-identify with the last supplied session. Dispose the client during SDK shutdown.
- Use
JObjectfor presence and lobby payloads in new integrations. Current convenience DTOs do not cover every live backend shape.
using Cysharp.Threading.Tasks;
using KiwiStudios.Sdk;
using Newtonsoft.Json.Linq;
using System.Threading;
using UnityEngine;
public sealed class KiwiRealtimeBootstrap
{
public async UniTask Connect(string _userId, string _token, CancellationToken _ct)
{
KiwiSdk.Realtime.OnAuthFailed += _error => Debug.LogError(_error);
KiwiSdk.Realtime.On("lobby:members_updated", _json =>
{
JObject _lobby = KiwiRealtimeClient.DeserializePayload<JObject>(_json);
Debug.Log(_lobby);
});
KiwiResponse _response = await KiwiSdk.Realtime.ConnectAndIdentify(_userId, _token, _ct);
if (!_response.Result)
{
Debug.LogError(_response.Message);
}
}
}
Queue options, game-mode fields, ranked rules, allocation payloads, and several response shapes still reflect the current first-party integration. Validate the exact wire payloads in a test environment before building reusable gameplay UI around them.
Trusted server
Consume the one-time match key once
The allocated match supplies a match ID and one-time match authentication key. The report must match the allocated app and environment and arrive while the match is running. Duplicate player IDs are rejected.
var _report = new KiwiMatchReportPayload
{
MatchId = _matchId,
MatchAuthKey = _matchAuthKey,
GameMode = _gameMode,
Map = _map,
AnalyticsData = _analyticsData,
GameData = _gameData
};
KiwiResponse _response = await KiwiSdk.MatchReport.SendReport(_report);
The package writes SdkAppId and Environment from the active config before sending. Match and player payload models remain tied to the current first-party allocation contract.
Reference
Check the result before reading data
Write-style calls return KiwiResponse. Typed reads return KiwiResult<T>. A failed response is data, not an exception contract: inspect Result, retain StatusCode, and show a product-safe message.
KiwiResult<KiwiCloudSaveLoadResponse> _result =
await KiwiSdk.CloudSave.LoadUserData(_userId, _token);
if (!_result.Result)
{
Debug.LogWarning($"cloud save failed ({_result.StatusCode}): {_result.Error}");
return;
}
KiwiCloudSaveLoadResponse _save = _result.Data;
The package retries transient failures only for retry-safe reads, using bounded exponential backoff. Authentication, analytics, increments, uploads, match reports, and other side-effecting writes are not retried automatically. Treat validation failures, quota errors, cloud-save 409 conflicts, and consumed match keys as state that requires a different action.
Reference
Know which side is authoritative
| Value or surface | Boundary |
|---|---|
| SDK app ID and environment | Public client configuration; together they select the tenant boundary. |
| Player session token | Sensitive account credential. KiwiSecureSessionStore uses native platform credential storage; PlayerPrefs remains compatibility-only. |
| Match authentication key | One-time game-server credential for one allocated match report. |
| Protected user data | Client-writable in the current Kiwi contract. |
| Private user data | Not player-writable through the normal endpoint, but returned to its owning player. |
| Achievements | Client-authoritative; unsuitable for trusted rewards. |
| Kiwi economy | First-party-only and unavailable to third-party SDK apps. |
- Never put secrets in remote config, analytics, logs, crash payloads, achievements, or cloud save.
- Validate game-impacting values on a trusted server before granting competitive or paid outcomes.
- Use separate apps for separate games and separate environments for the delivery lifecycle.
- Collect only the player data your game needs and disclose telemetry in your player-facing privacy materials.
Reference
Current v0.3.0 surface
| Client | Current use | Status |
|---|---|---|
Auth | Kiwi account registration, two-step login, rotating session restore, and opt-in offline identity | Preview |
RemoteConfig | Published environment JSON with encrypted local cache | Preview |
CloudSave | Shallow-merged user data and version-checked bounded file slots | Preview |
Analytics / Crash | Explicit events, queued batches, manual crash reports, and logs | Preview |
Achievements | Client-authoritative progress tracking | Limited trust |
Realtime / Friends / Presence / Lobby / Matchmaker | Socket.IO and first-party multiplayer foundations | First-party preview |
MatchReport | One-time authenticated report from an allocated game server | First-party preview |
Inventory | Kiwi wallet and inventory reads | First-party only |
Preview APIs, quotas, payloads, and availability can change before a stable release. Pin the package version and verify release notes before updating a project.
Support
Bring an app ID and a reproducible case
For package access or integration help, include the SDK version, Unity version, app ID, environment, failing client method, status code, and a redacted response. Never send a session token or match key.
Documentation version 0.3.0 · Updated July 11, 2026