View as Markdown

Session management

In the Sessions page, we described different scenarios setups to integrate Satori. This page goes into more details for each scenario and explains best practices.

Player-driven sessions (Client SDK owns sessions) #

This scenario applies to your project if you are using:

  • Satori only
  • A Nakama client (with or without websockets) and Satori

This setup reduces server-side complexity and scales well across global clients. Clients emit real-time game telemtry and analytics directly to Satori, resulting in a simple, low-latency solution. When integrated with Nakama, this setup ensures a unified player identity across both services.

Creating a session #

Sessions should be created client-side using Satori’s built-in authentication. When a user is authenticated through Satori, it automatically creates a session for the user. This should be done when the player opens the game or logs in.

Example using the Unity SDK:

1
2
3
4
5
6
7
8
9
const string scheme = "http";
const string host = "127.0.0.1";
const int port = 7450;
const string apiKey = "apiKey";

// Ensure you are using Client from the Satori namespace
var client = new Client(scheme, host, port, apiKey);

var session = await client.AuthenticateAsync("<user_id>");

For guidance on when to call AuthenticateAsync to keep session boundaries accurate, see When to start a new session.

Session refresh #

When a new session is created, Satori will return a session object containing:

  • Session Token and Expire Time
  • Refresh Token and Expire Time

If you reuse a stored session instead of authenticating, the SDK handles tokens automatically: it checks whether the session token is valid, silently refreshes it with the refresh token if needed, and returns an error requiring a fresh AuthenticateAsync once both have expired.

Most Satori client SDK libraries include a feature where sessions close to expiration are automatically refreshed. This is enabled by default but can be configured when first creating the Satori client using the following parameters:

  • AutoRefreshSession - Boolean value indicating if this feature is enabled, true by default
  • DefaultExpiredTimespan - The time prior to session expiry when auto-refresh will occur, set to 5 minutes be default

Configure token expiry to mirror player behavior #

Given that session refresh keeps a given session alive, it’s important to use token expiry settings as a safeguard in your session strategy. What we want to avoid is to end up with one 7-hour session which is rarely realistic especially on mobile platforms.

Use the token expiry settings to cap how long a single session can possibly run even when your boot and inactivity checks don’t fire.

SettingDefaultRole
session_token_expiry_sec129,600 (1.5 days)How long a session stays valid without a refresh
session_refresh_token_expiry_sec604,800 (7 days)How long the session can keep being extended by silent refresh

The maximum possible session length is the refresh token expiry plus one final session token lifetime: a refresh just before the refresh token expires grants one more full session token window.

Session configuration worked example #

A game has the following session targets:

  • Typical maximum session length: 6 hours
  • Absolute maximum session cap: 24 to 30 hours
  • Inactivity threshold: 30 minutes

Each target maps to one setting or client check:

TargetWhere it’s configuredValue
6 hour typical sessionsession_token_expiry_sec21600 (6 hours)
30 hour absolute capsession_refresh_token_expiry_sec86400 (24 hours: the cap minus one 6 hour token window)
30 minute inactivity thresholdClient code, not a server settingCall AuthenticateAsync after 30 minutes of inactivity
1
2
session_token_expiry_sec: 21600          # 6 hours: the typical maximum session length
session_refresh_token_expiry_sec: 86400  # 24 hours: the silent refresh window beyond the first token

With this configuration:

  1. Sessions stay valid for 6 hours without interruption.
  2. Sessions running past 6 hours refresh silently, with no gameplay interruption, up to a maximum of about 30 hours.
  3. Once both tokens expire, the next AuthenticateAsync creates a new session, again without interrupting gameplay. From that point it counts as the player’s next session. The token settings only catch what the boot and inactivity checks miss.

Sending events #

Events should be sent directly from the client to Satori using the event() method.

Example using the Unity SDK:

1
await client.EventAsync(session, new Event("gameFinished", DateTime.UtcNow));

Game-authoritative sessions (server-side) #

This scenario applies to your project if you are using:

  • Nakama server

This setup ensures data integrity for critical game mechanics by processing events through authoritative server logic. As a result, Satori receives validated, authoritative events from your Nakama server, complementing client-side data.

Creating sessions #

For events originating from the Nakama server (e.g., server-authoritative game logic), Satori sessions are typically created within Nakama’s server-side modules or webhooks.

Example using Nakama in Go:

1
2
3
satori := nk.GetSatori()
userId := "user-id"
satori.Authenticate(ctx, userId)

Sending events #

Events that require server-authoritative validation, complex logic, or are generated by Nakama itself (e.g., match results, economy updates, background events) should be sent from Nakama’s server-side code (modules or webhooks) to Satori.

Example using Nakama in Go:

1
2
3
4
5
6
7
8
9
satori.EventsPublish(ctx, "identityId", []*runtime.Event{{
  Name: "eventName",
  Id: "optionalEventId",
  Metadata: map[string]string{
    "someKey": "someValue",
  },
  Value: "someValue",
  Timestamp: time.Now().Unix(),
}})

Background events and system sessions #

The following steps describe your setup if you have or are using:

  • Third party servers
  • Background events (e.g., headless servers)
  • Non-player events (e.g., from the system, cron jobs, or other metrics)

With this setup, Satori has a comprehensive view of the game’s operations, including player events processed through Nakama (if applicable), and critical background events from your server infrastructure. It maintains a clear distinction between player and system events while allowing for independent data submission from backend systems. This also allows for advanced analytics around overall game health and the impact of server-side changes on player engagement.

Sending events #

Events from headless servers, background processes, or other third-party services should be sent directly from those servers to Satori using the POST /v1/server-event endpoint. These events might represent server-side simulations, bot activities, or external data integrations.

Example using the /v1/server-event API Endpoint:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
async function sendServerEvent(token, eventData) {
  const response = await fetch("http://your-satori-host:7450/v1/server-event", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${token}`, // Use the obtained token for authorization
    },
    body: JSON.stringify({ events: [eventData] }),
  });
  if (!response.ok) {
    const error = await response.json();
    console.error("Failed to send server event:", error);
  } else {
    console.log("Server event sent successfully!");
  }
}

Read more about sending server-side events in the Server Events API guide.

Conclusion #

Effective session management with Satori is crucial for gaining deep insights into player behavior and optimizing your live operations. By carefully considering where and how sessions are created and events are sent, you can ensure data consistency, minimize latency, and build a robust analytics pipeline that supports your game’s growth and player engagement strategies.