Game ideas & features

Your next feature

Player inbox

Targeted notifications and shared project broadcasts, with protected rewards and one social polling surface.

SDK developRequires a player session
Unity
Illustration of Player inbox
Your visible result

Your game displays targeted messages and broadcasts, marks them read, and claims each protected reward at most once.

Video coming later

This step does not have a video yet. The guide and MCP request are ready to use.

Player inbox

Prepare the same project

  1. Choose the same project in the dashboard as in your game configuration.

    Player inbox →
  2. No additional definition is needed for the first request. An empty result is valid until matching game data exists.

    Register the test player before publishing a plain-text broadcast in this project. Read the inbox with that same player.

    A broadcast reaches currently active players in this project who registered no later than publication. Previously inactive players can qualify after reactivation. Newer registrations are excluded.

Build this feature

You can preview the guide. Running it requires an active project key. Prepare project

SDK installed, project configuration imported and player signed in.

This SDK example uses develop. Check whether the required APIs exist in your installed SDK.

Complete the Quickstart connection with the intended player. Call RunReviewedAction() from an explicit button or scene action. Opening the file alone sends no request.

C#
Assets/Scripts/InboxDemo.cs
horizOn call
1using PM.horizOn.Cloud.Objects.Network.Responses;
2using UnityEngine;
3using PM.horizOn.Cloud.Core;
4using PM.horizOn.Cloud.Manager;
5​
6// Existing SDK/project configuration and authenticated player session required.
7// Call RunReviewedAction explicitly after reviewing this example and its data.
8// Use the current player's authenticated session. Retry an uncertain claim
9// with the same entry ID. Replace value state from the server; never apply
10// a reward delta twice.
11public sealed class InboxDemo : MonoBehaviour
12{
13 public async void RunReviewedAction()
14 {
15 var sessionUser = UserManager.Instance.CurrentUser;
16 if (sessionUser == null || !sessionUser.IsValid()) return;
17 var userId = UserManager.Instance.CurrentUser.UserId;
18 var manager = InboxManager.Instance;
19 var loaded = await manager.GetInbox(userId, since: null);
20 if (!loaded.IsSuccess) { Debug.LogWarning(loaded.ErrorCode); return; }
21 foreach (var entry in loaded.Data.entries) Debug.Log(entry.textKey);
22 // The manager retains full cached entries when the wire response is unchanged.
23 // Poll again only after loaded.Data.pollAfterSeconds, and stop with the game lifecycle.
24​
25 bool claimInFlight = false;
26 async System.Threading.Tasks.Task ClaimSelectedReward(string entryId)
27 {
28 if (claimInFlight) return;
29 const string projectId = "YOUR_PROJECT_ID";
30 string pendingKey = "horizOn_inbox_claim_" + projectId + "_" + userId;
31 string pending = PlayerPrefs.GetString(pendingKey, "");
32 if (pending.Length > 0 && pending != entryId) return; // Resolve the original claim first.
33 if (pending.Length == 0)
34 {
35 var current = manager.CurrentInbox; // Authorized for the current project/session.
36 if (current == null || current.userId != userId) return;
37 var selected = System.Array.Find(current.entries, entry => entry.id == entryId);
38 if (selected == null || selected.claimed || selected.attachment?.values == null ||
39 selected.attachment.values.Length == 0) return;
40 }
41 // Previously saved IDs remain replayable even after their visible state changes.
42 PlayerPrefs.SetString(pendingKey, entryId);
43 PlayerPrefs.Save();
44 if (PlayerPrefs.GetString(pendingKey) != entryId) return;
45 claimInFlight = true;
46 try
47 {
48 var claimed = await manager.Claim(userId, entryId);
49 if (!claimed.IsSuccess) { Debug.LogWarning(claimed.ErrorCode); return; }
50 // Display claimed.Data.inbox and replace values with claimed.Data.playerValues.
51 // The SDK refreshes its cache. Never add reward deltas yourself.
52 if (PlayerPrefs.GetString(pendingKey, "") == entryId)
53 {
54 PlayerPrefs.DeleteKey(pendingKey);
55 PlayerPrefs.Save();
56 }
57 }
58 finally { claimInFlight = false; }
59 }
60 // Invoke ClaimSelectedReward only from an explicit player action.
61 // Retry the saved entryId after a lost response; no replacement grant or new action ID.
62 // Other deliberate actions: await manager.MarkRead(userId, entryId);
63 // await manager.Remove(userId, entryId); // Protects unclaimed rewards.
64 }
65}
Open the complete SDK sample ↗

Test your new feature.

Your game displays targeted messages and broadcasts, marks them read, and claims each protected reward at most once.

Choose the same project in the dashboard as in your game configuration.

Endpoints
  • GET/inbox
  • POST/inbox/read
  • POST/inbox/claim
  • POST/inbox/remove
All endpoints in the API reference
Our suggestion for your next step

Give every round a high score.

Save the score after a round and show who's ahead. Keep using your connection and player session.