Docs

Set up RoSession

One ModuleScript records every session in your Roblox game, from join to leave, and why each player left. This page covers installing it, everything it tracks on its own, and the few calls that add your game's own events, funnels, currencies and Robux.

Quick start

  1. Sign in with Roblox and add your game on the dashboard. You get an API key and a guided setup that checks each step live. (During early access, new accounts join a waitlist first.)
  2. Insert the module into ServerStorage.
  3. Turn on Game Settings › Security › Allow HTTP Requests.
  4. Call RoSession.start from a server Script.
  5. Press Play. The first session shows up within about 10 seconds.

1. Add the module

Download RoSession.rbxmx. In Roblox Studio, right-click ServerStorage in the Explorer, choose Insert from File… and pick the file. You get a ModuleScript called RoSession with a LocalScript called RoSessionClient inside it.

Leave the LocalScript where it is. When RoSession starts it copies it into StarterPlayerScripts (and into anyone already playing), so it runs on every player's device and reports what the server can't see: FPS, idle time, device and open menus.

Prefer copy and paste? Create a ModuleScript named RoSession in ServerStorage with the source of RoSession.lua, and a LocalScript named RoSessionClient inside it with RoSessionClient.client.lua.

2. Allow HTTP requests

RoSession sends data with HttpService, the standard way experiences talk to web services. In Studio, open Home › Game Settings › Security, turn on Allow HTTP Requests and save. Game Settings needs the place to be published once (File › Publish to Roblox).

3. Start RoSession

Add a Script (not a LocalScript) to ServerScriptService and start RoSession with your key:

ServerScriptService/Analytics.server.lua
local RoSession = require(game.ServerStorage.RoSession)

RoSession.start({
    key = "rs_live_…",
    url = "https://www.rosession.com/api/v1/ingest",
})

Call it once, as early as you can, so the first players of every server are recorded. Options:

OptionTypeWhat it does
keystringYour game's API key (rs_live_…), from the dashboard. Server scripts aren't visible to players, so it's safe there.
secretstringInstead of key: the name of a Creator Hub secret holding the key (HttpService:GetSecret). Set the secret's domain to rosession.com.
urlstringWhere to send data. Defaults to https://rosession.com/api/v1/ingest.
studiobooleanfalse stops sending from Studio playtests. Default true (Studio data is kept apart and hidden unless you turn it on).
debugbooleantrue prints a line to Output for every batch sent.

Studio vs live data

Playtests in Studio are marked as Studio data. They're useful to check your setup but would skew real numbers, so the dashboard hides them unless you flip the Studio data switch. To stop sending from Studio entirely, pass studio = false.

What's tracked automatically

With only RoSession.start, you get:

SessionsEvery join and leave, length, new vs returning, join source (search, friend, invite, teleport), account age, Premium, country.
Why they leftA leave reason for every session, worked out from the signals below. See how leave reasons work.
BreadcrumbsThe last 15 things each player did before leaving (events, funnel steps, purchases, prompts).
Devices & performancePlatform (phone, tablet, desktop, console, VR), screen size, client FPS, ping, server FPS and memory.
ActivitySeconds spent idle, moving, AFK or in the Roblox menu, plus anything you set with RoSession.activity, and which tagged menus are open.
ServersEvery server's start, end, lifetime, peak players, end reason, and crashes (servers that stop sending).
ErrorsServer and client script errors with stack traces, rate-limited.
Purchase promptsEvery developer product and game pass prompt, bought or declined. Game pass purchases count as revenue automatically.

API reference

Everything is called on the server, after require(game.ServerStorage.RoSession). Calls are cheap: they add to a buffer that's sent every 10 seconds, and never yield or error your code.

RoSession.event(player, name, value?, fields?)

Anything players do. value is an optional number (a time, a score, an amount). fields is up to three values to break the event down by, as a list or with names. Events show up in the Event explorer and in each session's timeline.

Luau
boss.Defeated:Connect(function(player, timeTaken)
    RoSession.event(player, "BossDefeated", timeTaken, { boss = boss.Name, difficulty = "Hard" })
end)

RoSession.event(player, "PanelOpened", nil, { "Shop" }) -- "PanelOpened" also feeds the Menus chart

RoSession.funnel(player, funnelName, step, stepName)

A step in a flow: the tutorial, a quest, a shop. Number steps from 1. The Funnels page shows how many players reach each step and where they drop off.

Luau
local STEPS = { "Spawned", "Picked a class", "First kill", "Opened shop", "Finished tutorial" }
for i, name in STEPS do
    tutorial.StepReached[i]:Connect(function(player)
        RoSession.funnel(player, "Onboarding", i, name)
    end)
end

RoSession.currency(player, currency, amount, source)

In-game money moving. A positive amount is earned (a source), a negative one is spent (a sink). The Economy page shows sources, sinks and earning speed per currency, and players who earn nothing for a long time are marked Stalled.

Luau
RoSession.currency(player, "Coins", 250, "QuestReward")
RoSession.currency(player, "Coins", -1200, "BoughtSword")

RoSession.receipts(handler) → function

Wrap your ProcessReceipt and every developer product you grant is logged as revenue with the exact Robux spent. Re-delivered receipts are only counted once. Game passes and purchase prompts are tracked without this.

Luau
local MarketplaceService = game:GetService("MarketplaceService")

MarketplaceService.ProcessReceipt = RoSession.receipts(function(receipt)
    local granted = grantProduct(receipt) -- your code
    return if granted then Enum.ProductPurchaseDecision.PurchaseGranted else Enum.ProductPurchaseDecision.NotProcessedYet
end)

RoSession.variant(player, testName, variants) → string

Runs an A/B test. Give it a list of variants for an even split (the first is the control), or a table of weights (the biggest weight is the control). It returns the player's variant. The choice comes from a hash of their user id and the test name, so a player always gets the same variant, in every server and session, with nothing to save.

Script in ServerScriptService
local layout = RoSession.variant(player, "ShopLayout", { "Classic", "Bundles" })
if layout == "Bundles" then
	showBundleShop(player)
end

-- 80% keep the current reward, 20% try the new one
local reward = RoSession.variant(player, "DailyReward", { Coins = 80, Gems = 20 })

Call it wherever the variant matters; it's logged once per session as an Experiment event. The A/B tests page compares variants on playtime, sessions, paying players, Robux per player, and day 1 / week 1 return, counted from when each player first saw the test. A difference is marked significant at 95% confidence once each variant has 100+ players. To end a test, ship the winner and remove the call. Renaming a test starts a new one.

RoSession.profile(player, { level?, tutorialStep?, cash?, isNew? }) · RoSession.loaded(player)

Optional details that sharpen the reports. level powers the "where they quit, by level" chart; tutorialStep (0 once finished) powers tutorial drop-off and Quit during tutorial; isNew overrides RoSession's own first-visit detection. Call loaded when a player's save has loaded: then anyone who leaves before it's marked Left while loading.

Luau
Players.PlayerAdded:Connect(function(player)
    local data = loadData(player) -- your DataStore code
    RoSession.loaded(player)
    RoSession.profile(player, { level = data.Level, tutorialStep = data.TutorialStep })
end)

RoSession.activity(player, name?) · RoSession.task(player, description?)

activity says what someone is doing ("Fishing", "In a round"); it fills the "what players spend their time on" chart and Realtime. Without it RoSession still tells Idle, Moving and AFK apart. task marks something in progress; leaving before you clear it (pass nil) counts as Quit mid-task.

Luau
RoSession.activity(player, "Fishing")
RoSession.task(player, "Quest: find the key")
-- later
RoSession.task(player, nil)
RoSession.activity(player, nil)

Menus: tag a menu's Frame with the RoSessionMenu tag (Studio: Properties › Tags). While it's visible, players count as in that menu, labelled by the Frame's RoSessionMenu attribute or its name.

RoSession.crumb(player, text) · RoSession.kick(player, reason)

crumb adds a breadcrumb to the session without an event. kick kicks the player and records the reason, so the session is marked Kicked instead of a normal exit.

How leave reasons work

When someone leaves, RoSession sends the signals around their exit. The reason is worked out on our side, so the rules can improve without you updating your game, and the raw signals stay on each session. The first rule that matches wins:

Server shut downThe server closed (update, shutdown) with them in it.
KickedYour game kicked them through RoSession.kick.
Left while loadingYou call RoSession.loaded, and it never ran for them.
Bounced (< 1 min)Gone in under a minute.
Declined a purchase, then leftA purchase prompt was closed without buying in the 2 minutes before they left.
Quit during tutorialTheir tutorialStep (RoSession.profile) was above 0.
AFK / idleNo input for 3+ minutes before leaving.
Poor performanceAverage FPS under 20, or ping over 450 ms.
Script errors3 or more errors in the session.
Quit mid-taskA RoSession.task was still in progress.
Left from Roblox menuThe Roblox menu was open when they left.
Stalled (no progress)You track a currency, and they earned none for 5+ minutes of a 10+ minute session.
Left with a menu openOne of your tagged menus was open.
Long session ended30+ minutes and no warning signs: a good session.
Normal exitNone of the above.
Server lost / UnknownThe server stopped sending (crash), or no leave was ever reported.

Keys & team access

Each game has its own API keys under Settings. Only a fingerprint of a key is stored, so a key is shown once, when it's created. Make one per place if you like (keys can have labels), and revoke a key to stop it working within a minute.

To share a dashboard, add people by Roblox username in Settings › Who can see this game. They sign in with Roblox and see everything; only the owner can change settings, keys and members. Removing someone takes effect right away.

Discord alerts

In Settings › Discord alerts, paste a channel webhook (in Discord: channel settings › Integrations › Webhooks › New Webhook › Copy Webhook URL) and press Send test. RoSession checks your game every 5 minutes while it sends data, and posts when:

  • Player drop: players online fall below half of the last hour's average (when that average is 5+).
  • Error spike: 20+ errors in 15 minutes, and 3× the usual rate.
  • Crashing servers: 3+ servers stop responding within 15 minutes.
  • Leave-reason surge: a leave reason's share of the last 2 hours is double its share of the last week.
  • New versions: a new place version starts getting players, and its update impact report a day later.
  • Daily summary: yesterday's players, playtime, Robux and top leave reason, at an hour you pick (UTC).

Each alert waits 2 hours before it can post again, so one bad hour is one message. Turn alerts on or off one by one; clear the webhook to stop them all.

Update impact

Every session records the place version it ran on (Roblox bumps it each time you publish). The Update impact page compares a version with the one before it: players per day, session length, sessions per player, bounce rate, new players' day 1 return, paying players, Robux per player and errors per 100 sessions, plus how leave reasons shifted and which errors are new in that version.

Numbers are per player or per session, so a version that ran for a week compares fairly with one that ran for a day, but give a new version a day or two before reading much into it. Old servers keep running the previous version until they restart, so versions overlap for a while.

Why it moved

The Why it moved card on Overview compares the selected period with the one before it (same length) and ranks what most likely explains the change. It looks at:

  • Who changed: groups of players (platform, country, join source, new vs returning) that grew or shrank faster than everyone else.
  • Why they left: leave reasons whose share went up or down.
  • What broke: errors that are new this period, a higher error rate, and servers that crashed.
  • What shipped: place versions that went live, and notes on the timeline.
  • How it ran: FPS and ping by platform.
  • Money: whether Robux moved because of more (or fewer) paying players or how much each one spent, and which products changed most.

Pick Players, Playtime, Length, Robux or Day 1 to explain that number. The player-drop alert and the daily summary on Discord include the top causes too. These are leads to check, not proof: the dashboard pages they link to show the details.

Timeline & notes

Charts on Overview and Daily history show markers for what happened when. RoSession adds new place versions and A/B tests starting on its own; press Add note for anything else (an update, an event, an ad or promo). Anyone who can see the game can add notes. The owner can delete any note, and members can delete their own.

Click a marker, or its chip under the chart, to see what changed in the days after it against the same length of time before it, with the same ranked causes as Why it moved. Use it to check whether an ad, an event or an update actually worked.

Data & privacy

RoSession records gameplay data about your players: Roblox user id, username and display name, account age, Premium, country (from Roblox), device type and screen size, FPS, ping, what they did in your game (events you send, activity, menus) and purchases. It doesn't collect chat, passwords, real names or anything outside your experience, and nothing is sent from players' devices to RoSession directly: the client script reports to your server, which sends one batch every 10 seconds.

Deleting a game in Settings deletes all of its data. See the privacy policy and terms for the details, including your part: telling your players about analytics and following Roblox's terms.

Data retention

Raw data (individual events, server heartbeats and errors) is kept for 30 days by default. Sessions and servers are kept 3× as long, at least 90 days. Daily rollups and per-player totals are kept while the game exists, so Daily history and lifetime numbers don't disappear. Older rows are deleted once a day.

RoSession has a total storage limit. If it gets close, the raw-data window shrinks (never below 7 days) before anything else. Pages that read raw events, like the Event explorer, then only reach back that far.

Limits

  • Batches go out every 10 seconds, up to 800 events each. If RoSession can't be reached, up to 3,000 events wait and retry with backoff.
  • When a server shuts down, RoSession waits up to 20 seconds to send what's left.
  • One request carries up to 1,000 events and 2 MB; each server can send 120 requests a minute.
  • Event names are cut to 60 characters and fields to 60; up to 3 fields per event are used for breakdowns.
  • During early access: up to 20 games per account, 10 keys and 50 members per game.
  • Raw events are kept 30 days by default (see data retention).

Troubleshooting

Nothing shows up

Open Studio's Output window and look for [RoSession]. "Turn on … Allow HTTP Requests" means step 2 isn't done. "401 … Unknown or revoked API key" means the key is wrong or was revoked. No line at all usually means start is in a LocalScript, or the script never runs. debug = true prints every batch.

I only see data from Studio

Publish the place. Servers that were already running don't have the new code; they pick it up when they restart (you can shut down all servers from the Creator Hub).

Sessions show up, but no FPS, device or idle data

That comes from the RoSessionClient LocalScript. Check it's still inside the RoSession module, and that nothing deletes scripts from StarterPlayerScripts.

Robux revenue looks low

Developer products are counted through RoSession.receipts; make sure your ProcessReceipt is wrapped and returns PurchaseGranted. Revenue is gross Robux spent; Monetization also estimates your earnings after Roblox's cut.

Lots of "Unknown" or "Server lost" leave reasons

The server stopped sending before players left: a crash, or a shutdown that took longer than 20 seconds. Check the Servers page for when it happens.

AI agents & CLI

Ask about your games in plain words from Claude, ChatGPT, Cursor or your terminal. Answers come from your own RoSession data, and every answer links to the dashboard page it came from. Set it up on the AI & CLI page.

  • Connector (MCP): add https://www.rosession.com/mcp as a custom connector, then sign in with Roblox and press Allow. Claude Code: claude mcp add --transport http rosession https://www.rosession.com/mcp.
  • Command line: npx rosession login, then npx rosession overview "My Game". Pretty in a terminal, JSON when a program runs it (or with --json). npx rosession watch shows live players.
  • HTTP: GET https://www.rosession.com/api/agent/<tool>?game=… with Authorization: Bearer rsa_… (make a token on the AI & CLI page). /api/agent/tools lists the tools and their inputs.

Access is read-only and acts as you: it sees the games you can open, and can't change settings, keys, members or notes. Each token or connected app gets 2,000 lookups a day (live doesn't count). Revoke any of them on the AI & CLI page. Every reply looks like this:

Reply
{ "status": "ok", "tool": "overview", "game": { "id": "…", "name": "My Game" },
  "period": { "from": "2026-10-12T00:00:00Z", "to": "2026-10-19T00:00:00Z" },
  "data": { "units": "…", … },
  "link": "https://www.rosession.com/dashboard/…",
  "next": [{ "tool": "explain", "input": { "game": "…" }, "command": "rosession explain "My Game"", "when": "Why the numbers moved" }],
  "usage": { "used": 3, "limit": 2000 } }

status is ok or error, with a reason: login_required, not_found, missing_arguments, invalid_arguments, rate_limited or busy. Most tools take game (a name or id), range (24h, 7d, 30d… or from / to) and the dashboard's filters.

gamesThe Roblox games you can see on RoSession (yours and ones shared with you): id, name, your role, players online now, players and sessions in the last 24 hours, and when data last arrived. Start here when you don't know the game's id.
overviewHow a game did over a period, against the period before: players, new players, sessions, average session, playtime, Robux, paying players, peak and average players online, day-1 and day-7 retention, plus the top leave reasons, platforms and events. Start here for 'how is my game doing'.
explainExplains why a number changed against the previous period of the same length, ranked by how much each cause explains: groups of players who moved more than everyone else (platform, country, join source, new vs returning), leave reasons whose share grew, errors new this period, crashing servers, versions that went live, notes on the timeline, FPS and ping by platform, and paying players vs spend per payer. Use it for any 'why did players / playtime / Robux go down (or up)' question.
timelineWhat happened when: notes the team added (updates, events, ads, promos), new place versions going live and A/B tests starting, in the period. With at (a time from the list), what changed in the days after that moment against the same length of time before it, with the likely causes. Use it for 'did the ad / event / update work?'.
liveRight now: players online, live servers, who's playing (activity, time in session, device, idle, FPS), players online minute by minute over the last hour, today's totals and the latest events. Light: doesn't count toward the daily limit, made for asking again every 10-15 seconds.
leavingWhy sessions ended: every leave reason (worked out from signals like idling, errors, low FPS, declined purchases, quitting mid-tutorial or mid-task) with its share, average session and how many were new players; how long sessions survive minute by minute; the tutorial steps people quit at; the last things players did before leaving; and what they were doing.
retentionDay 1, 3, 7, 14 and 30 retention overall and by cohort (players who first joined in the same day or week), and new vs returning players over time.
engagementHow people play: session lengths, time per activity, menus opened, the most common events, busiest hours (UTC), and players, sessions and average session by platform, country, join source and new vs returning, plus the level spread.
sessionsIndividual sessions, newest first (or by length / Robux / level): who, when, how long, device, country, level, Robux, why they left and what they last did. Filter by leave reason or search a player.
sessionEverything about one session: the player, device, join source, server, level and currency before and after, Robux, activity time, the last breadcrumbs before leaving, its events and errors, and the leave reason with the detail behind it.
playerOne player across all their sessions: first and last seen, sessions, playtime, Robux, level, active days, purchases and their recent sessions with leave reasons.
playersPlayers of the game, by most recent, most playtime, most Robux, most sessions or highest level.
monetizationRobux: revenue, paying players, conversion, revenue per paying player and per daily player, estimated earnings after Roblox's cut, revenue over time, best products, purchase prompts shown vs bought, the latest purchases and what people buy first.
economyAn in-game currency: where it comes from (sources) and where it goes (sinks), earned vs spent over time, and earned per hour of play.
errorsScript errors (server and client) with counts, players affected, first and last seen and a trace; errors over time; server FPS, memory and ping over time; client FPS and ping by platform; and the FPS spread.
serversServers: how many, how long they live, peak and average players, crashes (servers that stopped responding), why they ended, and the most recent servers.
funnelsEvery funnel the game reports (like onboarding or a quest): each step, how many players and attempts reached it, and the drop-off between steps.
eventsThe game's custom events: which exist and how often they fire, and for one event its count, players and value over time, broken down by one of its fields, with recent samples.
experimentsEvery A/B test (RoSession.variant) in the period: each variant's players, playtime, sessions, paying players, Robux per player and day-1 / week-1 return, measured from when each player first saw the test, with p-values against the control. Significant means p < 0.05 with 100+ players per variant.
updatesHow a place version did against the one before it: players per day, session length, sessions per player, bounce rate, new players' day-1 return, paying players, Robux per player, errors per 100 sessions, how leave reasons shifted and which errors are new. Defaults to the latest version.
roblox_searchSearch the Roblox games RoSession tracks (any game, not just yours) by name, creator, universe id or place id. Returns universe ids for roblox_game.
roblox_gameAny tracked Roblox game: playing now (and when), its rank among tracked games and in its genre, 24h peak and average, peak seen since tracking began, visits, favorites, likes, rating, release and update dates, related games, and its player-count history (24h snapshots, 7d hourly, or daily).
roblox_rankingsRankings of tracked Roblox games: most-played (Highest player count right now, among games refreshed in the last 3 hours.) rising-24h (Players now against the same time yesterday (the nearest snapshot within 90 minutes). Games with 100+ players yesterday; tracked games only.) rising-7d (Yesterday's average players against the average 7 days before (UTC days with 6+ snapshots each). Tracked games only.) new (Released in the last 30 days (Roblox's created date), by players now.) top-rated (Likes as a share of likes + dislikes, for games with 10,000+ votes and 100+ players now.) most-visited (Roblox's all-time visits counter.) most-favorited (Roblox's favorites counter.)
roblox_compareTwo to five tracked Roblox games side by side: playing, peak seen, visits, favorites, rating, genre, dates, and each one's player-count history.

HTTP ingest API

The module does all of this for you; it's here if you want to send data from somewhere else.

Request
POST https://www.rosession.com/api/v1/ingest
x-api-key: rs_live_…
Content-Type: application/json

{
  "server": { "id": "<JobId>", "placeId": 123, "placeVersion": 42, "studio": false,
              "private": false, "maxPlayers": 12, "started": 1767225600000 },
  "events": [
    { "t": 1767225601000, "n": "session_start", "u": 1001, "s": "<session id>",
      "d": { "name": "builderman", "display": "builderman" } },
    { "t": 1767225650000, "n": "BossDefeated", "u": 1001, "s": "<session id>",
      "v": 42, "f": ["Golem", "Hard"] }
  ]
}

t is Unix time in milliseconds, n the event name, u the Roblox user id, s your session id. Responses: 200 { ok, accepted }, 401 bad key, 400 bad body, 413 too large, 429 slow down. Built-in event names:

session_startu, s, d: { name, display, accountAge, premium, joinSource, isNew?, level?, cash?, tutorialStep? }
session_metas, d: { country?, level?, cash?, tutorialStep?, isNew?, loaded?, inGroup?, friends? }
session_ends, d: { shutdown?, kicked?, loaded?, level?, earned?, spent?, tutorialStep?, idle?, inTask?, menu?, activity?, noProgress?, errors?, lastPrompt?: { key, bought, ago }, breadcrumbs?: [[secondsAgo, text]] }
client_infos, d: { platform, device }
client_ticks, d: { fps?, ping?, idle?, activity?, act?: { name: seconds }, level?, cash?, tutorialStep? }
server_start / server_tick / server_endd: { maxPlayers } / { players, fps, mem, ping, s: [session ids] } / { reason }
errord: { side: "server" | "client", message, trace }
Funnel · Economy · Purchase · PromptClosed · anything elseu, s, v (number), f: [f1, f2, f3], d (object)
Docs · RoSession