Dashboard
On this page

LiveOps SDK reference

Send events from your game and read remote config over HTTPS. Endpoints, keys, event fields, limits, errors and copy-paste code for every engine.

The SDK is a small piece of code in your game that does three things: batches events and sends them every 15 seconds or so, tracks sessions, and fetches remote config with the player's A/B test variants. There's nothing to install: the code for each engine is below and on your game's SDK setup tab, already filled in with your key. Anything that can make an HTTPS request can use it.

Your key

Every game has its own key, starting with dn_live_. Find it on the game's SDK setup tab, where you can show, copy or replace it.

  • The key can only send events and read that game's config. It can't read your numbers or change anything, so it's fine inside a game build.
  • On Roblox, keep it in server scripts anyway, away from the client.
  • If it leaks and someone sends junk, select New key. The old key stops working right away, so update your game first.
  • Archiving a game turns its key off.

Send the key in any of these ways. Use the first one your engine supports:

Where Example
X-DevNet-Key header X-DevNet-Key: dn_live_…
Authorization header Authorization: Bearer dn_live_…
key field in the JSON body (events only) {"key": "dn_live_…", "events": […]}
key query parameter …/api/liveops/v1/config?key=dn_live_…

Send events

POST https://devnet.lol/api/liveops/v1/events
Content-Type: application/json
X-DevNet-Key: dn_live_your_key_here

{
  "events": [
    { "name": "session_start", "player": "player-123" },
    { "name": "purchase", "player": "player-123", "value": 4.99, "props": { "item": "starter_pack", "currency": "usd" } }
  ]
}

Send up to 100 events per request in events, or a single event object on its own. The reply says how many were stored:

{ "ok": true, "accepted": 2, "rejected": 0 }

A bad event is skipped and counted in rejected; it never fails the rest of the batch.

Event fields

Field Required What it is
name Yes The event name. Stored in lowercase. 1 to 64 characters: letters, numbers and _ . : $ -.
player Yes A stable ID for the player, as a string or number, up to 128 characters. playerId and userId also work.
value No A number: the session length in seconds, a price, a level.
session No A session ID, up to 128 characters, if you track sessions yourself. sessionId also works.
props No A JSON object of extra details, up to 2,048 bytes. Larger props are dropped, but the event is kept.
time No When it happened: Unix seconds, Unix milliseconds or an ISO 8601 date. timestamp also works. Times up to 7 days old are kept, so offline queues work. Times more than 5 minutes in the future, older than 7 days or unreadable are replaced with the time DevNet received the event.

Use a player ID that doesn't change: the Roblox UserId, Minecraft UUID, SteamID64 or your own account ID. Retention, funnels and A/B tests all depend on it.

Events DevNet understands

These four power the dashboard's numbers. Anything else is a custom event (tutorial_done, boss_killed, shop_opened…) that you can use in funnels and as an A/B test goal.

Event When to send it
session_start A player joined. Starts a session.
session_end A player left. Send the session length in seconds as the value.
purchase A player bought something. Send the price as the value and the currency in props.
level_up Progress milestone. Send the new level as the value.

From these, DevNet works out each day's players, new players, sessions, playtime, average session length, revenue, purchases, paying players, and D1 and D7 retention. Sessions are counted by session when you send one, or by session_start events when you don't. Revenue is the sum of purchase values: the dashboard shows it in Robux for Roblox games and in US dollars for everything else, so send prices in that currency.

Read remote config

GET https://devnet.lol/api/liveops/v1/config?player=player-123
X-DevNet-Key: dn_live_your_key_here
{
  "ok": true,
  "config": { "halloween_enabled": true, "starter_pack_price": 49 },
  "experiments": { "starter_pack_price_test": "test" },
  "fetchedAt": "2026-10-30T12:00:00.000Z"
}
  • config is every key from the game's remote config, with the player's A/B test overrides applied on top.
  • experiments maps each running test's key to the variant this player is in.
  • Pass player (up to 128 characters) to get that player's variants. Without it you get the plain config and no variants.

Config is cached for up to 30 seconds. If the call fails, keep using the last values you had and try again later. Always ship defaults in your code for when a key is missing.

Limits and errors

  • Up to 100 events and 256,000 characters per request.
  • About 120 requests a minute per game. Batch events and send every 10 to 30 seconds; the snippets below send every 15.
  • Raw events are kept for 90 days. The daily numbers made from them are kept after that.
  • New events show up in the dashboard's daily numbers within about 10 minutes.
  • Both endpoints allow requests from any origin, so browser builds (Unity WebGL, Godot HTML5) work.
Status Meaning What to do
200 Stored (events) or here's the config. Nothing.
400 The body isn't JSON. Send a JSON object.
401 The key is missing, wrong, replaced, or its game is archived. Check the key on the SDK setup tab.
413 More than 100 events, or the body is too large. Send smaller batches.
429 Too many requests for this game. Batch events and send less often.
500 DevNet couldn't store or load it. Retry shortly. For config, keep your last values.

Code for your engine

Each snippet sends session_start and session_end for you and gives you a track call for everything else. Swap in your own key, or copy the filled-in version from your game's SDK setup tab.

ServerScriptService › DevNet (ModuleScript)

-- DevNet LiveOps for Roblox. Put this ModuleScript in ServerScriptService.
local HttpService = game:GetService("HttpService")
local Players = game:GetService("Players")

local KEY = "dn_live_your_key_here"
local EVENTS_URL = "https://devnet.lol/api/liveops/v1/events"
local CONFIG_URL = "https://devnet.lol/api/liveops/v1/config"

local DevNet = { config = {}, experiments = {} }
local queue, joinedAt = {}, {}

function DevNet.track(player, name, value, props)
	table.insert(queue, {
		name = name,
		player = tostring(typeof(player) == "Instance" and player.UserId or player),
		value = value,
		props = props,
		time = os.time(),
	})
end

local function flush()
	if #queue == 0 then return end
	local batch = queue
	queue = {}
	local ok, err = pcall(function()
		HttpService:RequestAsync({
			Url = EVENTS_URL,
			Method = "POST",
			Headers = { ["Content-Type"] = "application/json", ["X-DevNet-Key"] = KEY },
			Body = HttpService:JSONEncode({ events = batch }),
		})
	end)
	if not ok then warn("[DevNet] send failed:", err) end
end

-- Remote config, with this player's A/B test variants applied
function DevNet.getConfig(player)
	local url = CONFIG_URL .. "?player=" .. tostring(player and player.UserId or "")
	local ok, res = pcall(function()
		return HttpService:RequestAsync({ Url = url, Method = "GET", Headers = { ["X-DevNet-Key"] = KEY } })
	end)
	if ok and res.Success then
		local data = HttpService:JSONDecode(res.Body)
		return data.config, data.experiments
	end
	return DevNet.config, {}
end

Players.PlayerAdded:Connect(function(player)
	joinedAt[player] = os.clock()
	DevNet.track(player, "session_start")
end)
Players.PlayerRemoving:Connect(function(player)
	DevNet.track(player, "session_end", math.floor(os.clock() - (joinedAt[player] or os.clock())))
	joinedAt[player] = nil
end)
game:BindToClose(flush)

task.spawn(function()
	DevNet.config = DevNet.getConfig(nil)
	while true do
		task.wait(15)
		flush()
	end
end)

return DevNet

-- Usage, from any server script:
-- local DevNet = require(game.ServerScriptService.DevNet)
-- DevNet.track(player, "purchase", 99, { item = "Speed Coil", currency = "robux" })

Turn on Game Settings › Security › Allow HTTP Requests. Require it from a server Script; keep the key out of client code.

Fortnite (UEFN)

Verse can't make web requests, so there's no SDK for Fortnite islands. LiveOps reads their numbers from Epic's public island data instead. See Fortnite islands.

Still stuck? Ask in the support channel on the DevNet Discord, or see how support works on your plan.