PlaySync SDK

One Title ID. Every feature is one function call.

Installation

Drop the playsync-unity-sdk folder into your project's Assets/ folder. No package manager needed.

Setup

// One line. That's it.
PlaySyncSettings.TitleId = "YOUR_TITLE_ID";

Get your Title ID from the dashboard when you create a title.

Device Login

Logs in with hardware device ID. Creates account automatically on first login.

PlaySyncAPI.Auth.LoginWithDevice(
    new LoginWithDeviceIdRequest { CreateAccount = true },
    result => Debug.Log($"Player: {result.PlaySyncId}"),
    error => Debug.LogError(error.ErrorMessage)
);

Register

PlaySyncAPI.Auth.Register(
    "PlayerOne", "mypassword123",
    result => Debug.Log($"Registered: {result.PlaySyncId}"),
    error => Debug.LogError(error.ErrorMessage),
    email: "optional@email.com"
);

Username Login

PlaySyncAPI.Auth.Login(
    "PlayerOne", "mypassword123",
    result => Debug.Log("Logged in!"),
    error => Debug.LogError(error.ErrorMessage)
);

Session Management

// Check login state
if (PlaySyncAPI.Auth.IsLoggedIn)
    Debug.Log(PlaySyncAPI.Auth.PlayerId);

// Refresh expired token
PlaySyncAPI.Auth.RefreshSession(ok => {}, err => {});

// Logout
PlaySyncAPI.Auth.Logout();

Save Data

// Single key
PlaySyncAPI.Data.SetData("inventory", jsonString,
    result => Debug.Log($"Saved v{result.version}"),
    error => Debug.LogError(error.ErrorMessage)
);

// Multiple keys at once
PlaySyncAPI.Data.SetData(
    new UpdatePlayerDataRequest {
        Data = new Dictionary<string, string> {
            { "level", "5" },
            { "xp", "2400" }
        }
    },
    result => Debug.Log($"Saved {result.KeysUpdated} keys"),
    error => Debug.LogError(error.ErrorMessage)
);

Load Data

// Single key
PlaySyncAPI.Data.GetData("inventory",
    result => Debug.Log(result.value),
    error => Debug.LogError(error.ErrorMessage)
);

// Multiple keys
PlaySyncAPI.Data.GetData(
    new GetPlayerDataRequest {
        Keys = new List<string> { "level", "xp" }
    },
    result => {
        string level = result.Data["level"].Value;
    },
    error => Debug.LogError(error.ErrorMessage)
);

Delete Data

PlaySyncAPI.Data.DeleteData("old_key",
    result => Debug.Log("Deleted"),
    error => Debug.LogError(error.ErrorMessage)
);

List Keys

PlaySyncAPI.Data.ListKeys(
    result => {
        foreach (var key in result.keys)
            Debug.Log(key);
    },
    error => Debug.LogError(error.ErrorMessage)
);

Get Balance

PlaySyncAPI.Currency.GetBalance("CS",
    result => Debug.Log($"Coins: {result.Balance}"),
    error => Debug.LogError(error.ErrorMessage)
);

Daily Reward

Server validates the 24-hour timer. Players can't cheat it by changing their device clock.

PlaySyncAPI.Currency.ClaimDaily(
    result => {
        if (result.granted)
            Debug.Log($"Got {result.amount} coins! Balance: {result.balance}");
        else
            Debug.Log(result.message); // "Next claim in 5h 23m"
    },
    error => Debug.LogError(error.ErrorMessage)
);

Browse Catalog

No login needed — catalog is public.

PlaySyncAPI.Inventory.GetCatalog(
    result => {
        foreach (var item in result.items)
            Debug.Log($"{item.display_name}: {item.price_amount} {item.price_currency}");
    },
    error => Debug.LogError(error.ErrorMessage)
);

Purchase Item

Server checks balance, deducts currency, and grants item — all in one call, can't be cheated.

PlaySyncAPI.Inventory.Purchase("red_hat",
    result => {
        Debug.Log($"Bought! Spent {result.currency_spent}, balance: {result.currency_balance}");
    },
    error => {
        // error.ErrorMessage = "Not enough CS. Need 500, have 200."
        Debug.LogError(error.ErrorMessage);
    }
);

Get My Items

PlaySyncAPI.Inventory.GetMyItems(
    result => {
        foreach (var item in result.items)
            Debug.Log($"Own: {item.display_name} x{item.quantity}");
    },
    error => Debug.LogError(error.ErrorMessage)
);

Check If Player Owns Item

PlaySyncAPI.Inventory.HasItem("red_hat",
    owns => {
        if (owns) EquipHat();
    },
    error => Debug.LogError(error.ErrorMessage)
);

Get Config Value

No login needed — config is public. Set values from the dashboard.

PlaySyncAPI.Config.GetValue("ServerMessage",
    result => motdText.text = result.value,
    error => Debug.LogError(error.ErrorMessage)
);

Get All Config

Returns a ConfigMap with typed helper methods.

PlaySyncAPI.Config.GetAll(
    config => {
        string motd = config.Get("ServerMessage");
        bool maintenance = config.GetBool("MaintenanceMode");
        int minVer = config.GetInt("MinVersion", 1);
        float discount = config.GetFloat("StoreDiscount", 0);
        bool exists = config.Has("SomeKey");
    },
    error => Debug.LogError(error.ErrorMessage)
);

Execute CloudScript

Fetch a script by name. Write and manage scripts in the dashboard code editor.

PlaySyncAPI.CloudScript.Execute("DailyRewards",
    result => {
        Debug.Log($"Script: {result.name} v{result.revision}");
        Debug.Log(result.code);
    },
    error => Debug.LogError(error.ErrorMessage)
);

Submit Score

Only saves if it's the player's personal best.

PlaySyncAPI.Leaderboards.SubmitScore("high_scores", 42000f,
    result => {
        if (result.updated)
            Debug.Log($"New best: {result.best}");
    },
    error => Debug.LogError(error.ErrorMessage)
);

Get Rankings

PlaySyncAPI.Leaderboards.GetLeaderboard("high_scores", 10,
    result => {
        foreach (var e in result.Entries)
            Debug.Log($"#{e.Position} {e.DisplayName}: {e.StatValue}");
    },
    error => Debug.LogError(error.ErrorMessage)
);

Error Handling

Every API call uses the same error callback pattern.

void OnError(PlaySyncError error)
{
    if (error.IsBanned)
        ShowBanScreen();
    else if (error.IsRateLimited)
        StartCoroutine(RetryLater());
    else if (error.IsTokenExpired)
        PlaySyncAPI.Auth.RefreshSession(ok => {}, err => ForceLogin());
    else
        Debug.LogError(error.ErrorMessage);
}
PropertyTypeWhen
IsBannedboolPlayer banned (403)
IsRateLimitedboolToo many requests (429)
IsTokenExpiredboolJWT expired (401)
HttpCodeintRaw status code
ErrorMessagestringHuman-readable message