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
- 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.)
- Insert the module into ServerStorage.
- Turn on Game Settings › Security › Allow HTTP Requests.
- Call RoSession.start from a server Script.
- 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.
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:
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:
| Option | Type | What it does |
|---|---|---|
| key | string | Your game's API key (rs_live_…), from the dashboard. Server scripts aren't visible to players, so it's safe there. |
| secret | string | Instead of key: the name of a Creator Hub secret holding the key (HttpService:GetSecret). Set the secret's domain to rosession.com. |
| url | string | Where to send data. Defaults to https://rosession.com/api/v1/ingest. |
| studio | boolean | false stops sending from Studio playtests. Default true (Studio data is kept apart and hidden unless you turn it on). |
| debug | boolean | true 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:
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.
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 chartRoSession.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.
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)
endRoSession.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.
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.
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.
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.
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.
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 down | The server closed (update, shutdown) with them in it. |
| Kicked | Your game kicked them through RoSession.kick. |
| Left while loading | You call RoSession.loaded, and it never ran for them. |
| Bounced (< 1 min) | Gone in under a minute. |
| Declined a purchase, then left | A purchase prompt was closed without buying in the 2 minutes before they left. |
| Quit during tutorial | Their tutorialStep (RoSession.profile) was above 0. |
| AFK / idle | No input for 3+ minutes before leaving. |
| Poor performance | Average FPS under 20, or ping over 450 ms. |
| Script errors | 3 or more errors in the session. |
| Quit mid-task | A RoSession.task was still in progress. |
| Left from Roblox menu | The 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 open | One of your tagged menus was open. |
| Long session ended | 30+ minutes and no warning signs: a good session. |
| Normal exit | None of the above. |
| Server lost / Unknown | The 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:
{ "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.
| games | The 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. |
| overview | How 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'. |
| explain | Explains 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. |
| timeline | What 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?'. |
| live | Right 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. |
| leaving | Why 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. |
| retention | Day 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. |
| engagement | How 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. |
| sessions | Individual 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. |
| session | Everything 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. |
| player | One player across all their sessions: first and last seen, sessions, playtime, Robux, level, active days, purchases and their recent sessions with leave reasons. |
| players | Players of the game, by most recent, most playtime, most Robux, most sessions or highest level. |
| monetization | Robux: 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. |
| economy | An in-game currency: where it comes from (sources) and where it goes (sinks), earned vs spent over time, and earned per hour of play. |
| errors | Script 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. |
| servers | Servers: how many, how long they live, peak and average players, crashes (servers that stopped responding), why they ended, and the most recent servers. |
| funnels | Every funnel the game reports (like onboarding or a quest): each step, how many players and attempts reached it, and the drop-off between steps. |
| events | The 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. |
| experiments | Every 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. |
| updates | How 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_search | Search 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_game | Any 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_rankings | Rankings 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_compare | Two 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.
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_start | u, s, d: { name, display, accountAge, premium, joinSource, isNew?, level?, cash?, tutorialStep? } |
| session_meta | s, d: { country?, level?, cash?, tutorialStep?, isNew?, loaded?, inGroup?, friends? } |
| session_end | s, d: { shutdown?, kicked?, loaded?, level?, earned?, spent?, tutorialStep?, idle?, inTask?, menu?, activity?, noProgress?, errors?, lastPrompt?: { key, bought, ago }, breadcrumbs?: [[secondsAgo, text]] } |
| client_info | s, d: { platform, device } |
| client_tick | s, d: { fps?, ping?, idle?, activity?, act?: { name: seconds }, level?, cash?, tutorialStep? } |
| server_start / server_tick / server_end | d: { maxPlayers } / { players, fps, mem, ping, s: [session ids] } / { reason } |
| error | d: { side: "server" | "client", message, trace } |
| Funnel · Economy · Purchase · PromptClosed · anything else | u, s, v (number), f: [f1, f2, f3], d (object) |