Skip to documentation
Kiwi SDK Docs
Now reading Overview
Open SDK Portal

Unity backend services

Build connected Unity games with Kiwi SDK.

Use Kiwi accounts, cloud saves, remote config, analytics, diagnostics, and evolving multiplayer foundations from one app-and-environment scoped SDK.

Developer preview v0.3.0 Unity 2022.3+

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.

Isolation is part of the contract.Data written to dev is separate from staging and production. The app ID and environment travel with each SDK request.
Ship safer changesPublish environment-specific JSON and load an encrypted remote-config cache before the network responds.
Keep player stateStore small JSON documents or versioned file slots with integrity checks and bounded quotas.
See what happenedSend explicit analytics events, crash payloads, and logs without installing a separate reporting package.
Build on Kiwi accountsUse the same two-step login and rotating session used by Kiwi Studios services.

Recommended integration path

  1. 1
    Create an SDK app

    Open the SDK Portal, create an app, and copy its public app ID.

  2. 2
    Install the tagged package

    Add the Kiwi SDK v0.3.0 Git URL and its two direct Git dependencies to the Unity project manifest.

  3. 3
    Start in dev

    Initialize against dev, authenticate a test account, and verify each service independently.

  4. 4
    Promote 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.

Pin the release tag.Use kiwi-sdk-v0.3.0 rather than an unversioned branch so package updates remain deliberate and reproducible.

Required packages

PackagePurposeDeclared version
com.cysharp.unitaskAsync Unity operations2.5.10
com.itisnajim.socketiounityRealtime Socket.IO transport1.1.5
com.unity.nuget.newtonsoft-jsonJSON serialization3.2.2
com.unity.modules.unitywebrequestHTTP transport1.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

Packages/manifest.json
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

  1. Put the approved com.kiwistudios.sdk folder in a stable local location, normally your project's Packages directory.
  2. In Unity, open Window → Package Manager.
  3. Select +, then Add package from disk….
  4. Select com.kiwistudios.sdk/package.json.
Local package file
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.

Safe in a player buildSDK app ID, selected environment, and the Kiwi service origin.
Never put in a player buildMatch authentication keys or another player's session token.

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.

KiwiSdkBootstrap.cs
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();
	}
}
Use dev while integrating.Changing the environment changes the data boundary. It does not copy config, accounts' game data, or queues between environments.

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.

Restore a cached session
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;
	}
}
Offline identity is disabled by default.Use 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.

Remote config
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

Public

The signed-in player can write it. Use it for data that is safe to show outside the owner account.

Protected

The signed-in player can currently write it. Treat it as owner-scoped, client-authoritative data.

Private

Normal player writes cannot change it, but the owning player receives it when loading. It is not a secret store.

JSON saves shallow-merge top-level keys.A write does not replace the complete object, and nested objects are not deep-merged. Load the current Version and pass it to SaveUserDataVersioned; stale writes return 409 cloud_save_version_conflict.
Save and load player data
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.

Upload one slot file
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

BoundaryDefault
Slots per player, app, and environment10
Files per slot32
Decoded bytes per file512 KiB
Decoded bytes per slot700 KiB
Total slot bytes per player5 MiB
Serialized manifest64 KiB
Slot ID length96 characters
File path length191 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.

Achievement writes are client-authoritative.The backend currently accepts unlocks and increments without requiring a matching portal definition. Do not use achievement state to grant currency, paid items, competitive rank, or other trusted rewards.
Increment progress
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.

Queue and flush analytics
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);
Keep payloads deliberate.Do not submit credentials, match keys, full authorization headers, or unnecessary personal information. Crash and log payloads are visible to the owning SDK app in the portal.

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 or Off(eventName) to remove all.
  • ConnectAndIdentify returns a KiwiResponse, 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 JObject for presence and lobby payloads in new integrations. Current convenience DTOs do not cover every live backend shape.
Subscribe before identify
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.

The allocated roster must be complete.Duplicate player IDs and reports missing any allocated player are rejected before the match key is consumed. Unexpected IDs are logged as possible bots, the report may continue, and those IDs are excluded from authoritative player-stat writes.
A successful report consumes the key.Retries after acceptance return an already-used response. Persist enough server-side state to distinguish an accepted report from a network failure before retrying.
Submit from the game server
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.

Typed result handling
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 surfaceBoundary
SDK app ID and environmentPublic client configuration; together they select the tenant boundary.
Player session tokenSensitive account credential. KiwiSecureSessionStore uses native platform credential storage; PlayerPrefs remains compatibility-only.
Match authentication keyOne-time game-server credential for one allocated match report.
Protected user dataClient-writable in the current Kiwi contract.
Private user dataNot player-writable through the normal endpoint, but returned to its owning player.
AchievementsClient-authoritative; unsuitable for trusted rewards.
Kiwi economyFirst-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

ClientCurrent useStatus
AuthKiwi account registration, two-step login, rotating session restore, and opt-in offline identityPreview
RemoteConfigPublished environment JSON with encrypted local cachePreview
CloudSaveShallow-merged user data and version-checked bounded file slotsPreview
Analytics / CrashExplicit events, queued batches, manual crash reports, and logsPreview
AchievementsClient-authoritative progress trackingLimited trust
Realtime / Friends / Presence / Lobby / MatchmakerSocket.IO and first-party multiplayer foundationsFirst-party preview
MatchReportOne-time authenticated report from an allocated game serverFirst-party preview
InventoryKiwi wallet and inventory readsFirst-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

Kiwi SDK v0.3.0

Search documentation

Esc