Skip to Content

Live Config

Live Config lets you change game values while the game is running: lucky block drops and chances, shop prices, event switches, multipliers. You don’t publish a new version of the game.

It is built on Roblox’s native experience configs . Roblox stores the values and delivers them to every server. RoControl is the visual editor and publisher on top of them.

PieceRole
Roblox experience configsHold the values and deliver them to live servers in about 15 seconds to 1 minute after a publish, with no game update
RoControl Live ConfigA canvas editor for categories and items, with templates, images, chances and references. It publishes to Roblox for you
LiveConfig.luauA ModuleScript for your game: read values, roll drops, react to updates

Example: in Escape from Granny, lucky blocks drop brainrots with chances. The team changes a chance or adds a brainrot on the website, clicks Publish, and running servers use the new drop table within a minute.

How values reach your game

  1. You edit categories and items on the canvas. RoControl saves as you go and counts the changes that are not live yet.
  2. Publish writes one Roblox config key per category, plus a RoControl manifest key, to the experience’s InExperienceConfig repository, and publishes it.
  3. Roblox delivers the new values to running servers.
  4. The SDK refreshes, and LiveConfig.onChanged fires for the categories that changed.

Setup

  1. Open the game from the sidebar.
  2. Open Live Config. It is always available: there is no module switch.
  3. Choose how RoControl publishes to Roblox:
OptionWhat to do
A. Open Cloud API keyIn Creator Hub, create an API key with configs access: the universe read and write permissions (universe:read, universe:write), restricted to this experience. Paste it in Live Config → Roblox access (the shield button)
B. Connect Roblox (OAuth)Connect your Roblox account with the universe:read and universe:write scopes, and select this experience on Roblox’s consent screen

Reusing the Event Automation key. If the game already has an Event Automation API key, Live Config reuses it. Add the universe read and write permissions to that same key instead of creating a second one.

Option B needs the universe:read and universe:write scopes enabled on the RoControl OAuth app.

When more than one is set, RoControl publishes with the Live Config API key first, then the Roblox connection, then the Event Automation key. You can build categories before any of this; access is only needed to publish and to upload images.

Team permissions

ActionAllows
ViewSee the canvas and the publish history
EditChange categories, items, values and images
PublishPublish to Roblox and manage the API key or connection

See Team Permissions.

The canvas

Each category is a card on a canvas. Drag cards to arrange them; the layout is only for you and does not change what is published. Cards show the category’s and items’ images. Reference fields are drawn as links between categories, for example from LuckyBlocks to Brainrots.

Right-click for shortcuts:

Right-click onGives
A cardOpen, add item, rename, image, color, edit fields, copy key, delete
An item (on a card or in a category)Edit, image, duplicate, hide from game / show, copy id, delete
Empty canvasNew category or settings at that spot, a template there, fit everything

Collection and settings categories

KindHoldsPublished asRead in game with
CollectionA list of items. Each has an id, a name, an optional image and your fields{ "items": [ ... ] }LiveConfig.list, LiveConfig.get
SettingsOne record of values{ "coinMultiplier": 2, ... }LiveConfig.settings, LiveConfig.value
  • Category key: made from the name (Lucky Blocks becomes LuckyBlocks). It is the Roblox config key and the name your code uses. RoControl is reserved for the manifest.
  • Item id: made from the name (Golden Block becomes golden_block). Renaming an item keeps its id, so use the id in code and in saved player data. Change an id only while nothing refers to it yet: references and saved data keep the old one.
  • Disabled items (hidden from the game) are not published.

Templates

Templates add ready-to-fill categories in one click.

TemplateCategories
Lucky blocksBrainrots (rarity, income /s, sell price) and LuckyBlocks (price, rarity, drops: brainrot + chance)
Eggs and petsPets (rarity, multiplier) and Eggs (price, hatches: pet + chance)
ShopShop (price, currency Coins / Gems / Robux, on sale, description)
Game settingsGameSettings, a settings category: coin multiplier, luck boost, event active, announcement

If a key is already taken, the template adds a number (Brainrots2), and its references follow the new key.

Fields

TypeHoldsPublished as
TextNames, descriptions, messages"Double coins!"
NumberPrices, income, multipliers. Optional min, max and unit100
BooleanOn/off switchestrue
ImageA Roblox image asset"rbxassetid://123"
SelectOne of a few options, like a rarity"Epic"
ColorA hex color"#ff8800"
ReferenceAn item of another categoryThe item’s id, "tralalero"
ListRows with their own columns[ { "brainrot": "tralalero", "chance": 50 } ]

Fields with no value are left out of the published JSON (or take the field’s default when it has one). id, name, image and items are built in and can’t be used as field keys. Lists can’t contain lists.

Lists and chances

A list can name one of its number columns as its chance column, the drop weight of each row. The editor shows each row’s percent of the total.

Weights don’t need to add up to 100:

BrainrotChanceShown as
Tralalero5090.9%
Bombardiro59.1%

In the game, LiveConfig.roll and LiveConfig.rollDrop use the same weights, and LiveConfig.chances returns the same percentages.

Images

Images are Roblox image asset IDs, published as rbxassetid://ID strings that your game can put straight into ImageLabel.Image.

  • Paste an ID (123456789), an rbxassetid:// string, or a Creator Store or library link.
  • Upload a PNG, JPG, BMP or TGA image (up to 6 MB). RoControl uploads it to Roblox as a Decal owned by the experience’s owner. Uploading needs the Assets permission (asset:write) on the API key or Roblox connection.

Decal ID or Image ID? A Decal is its own asset that wraps an image, and the two IDs can differ. An ImageLabel may not show a Decal ID. For images used in UI, copy the Image ID from Studio’s Asset Manager and paste it.

Publishing

  • The header shows how many changes are pending: categories whose published value would change.
  • Publish opens a summary of the changes, with an optional message and a rollout choice:
RolloutEffect
ImmediateAll servers in about 15 seconds to 1 minute
GradualRolls out over about 15 minutes
  • Only changed keys are sent. Deleting a category removes its key from Roblox.
  • Conflict protection: before publishing, RoControl reads the live configs. If a key it manages was changed outside RoControl since its last publish (for example in Creator Hub), RoControl lists those keys and asks before overwriting them.
  • History: every publish is recorded with its version, message, changed keys and author. The publishes also appear on the Configs History page in Creator Hub.

Limits

LimitValue
Config keys per experience100. Each category uses one key, the manifest uses one, and configs created outside RoControl count too
Characters per value100,000 per category (its JSON). Split a category that is too large

Import from your game

Your game can fill the board with its own current values, so you don’t retype anything. A game server sends them on start with the game’s SDK key (the rocontrol_sdk HttpService secret the RoControl SDK already uses):

POST https://api-rocontrol.duckdns.org/api/sdk/v1/live-config/defaults Authorization: Bearer <SDK key> { "universeId": "123", "categories": [ { "key": "Brainrots", "name": "Brainrots", "kind": "collection", "fields": [ ... ], "items": [ { "key": "TungTungSahur", "name": "Tung Tung Sahur", "image": "rbxassetid://1", "values": { ... } } ] } ] }
  • An empty board is filled with it right away. Nothing changes in game until you press Publish.
  • Later reports show an Import from game button with what is missing. It adds missing categories and items and never overwrites your edits.
  • References to items the report doesn’t contain are dropped, and listed in the response’s warnings.
  • The report’s universeId must match the key’s game.

A game should keep its own values as the fallback and apply published ones on top, value by value. Escape From Granny’s LiveConfigBridge does this: a value that is missing, malformed or out of range keeps the game’s local value, and a LiveConfig category with enabled = false switches everything back to local.

Using it in your game

Install

  1. Download LiveConfig.luau from Use in your game (the >_ button in the Live Config toolbar), or from https://rocontrol.duckdns.org/sdk/LiveConfig.luau.
  2. In Studio, add a ModuleScript named LiveConfig to ReplicatedStorage and paste the file’s contents.
  3. Require it from any script:
local ReplicatedStorage = game:GetService("ReplicatedStorage") local LiveConfig = require(ReplicatedStorage.LiveConfig)

ConfigService only works on the server. The SDK reads it there, and mirrors the values to clients when the server calls LiveConfig.replicate().

Example: open a lucky block

A server Script that opens a block when a client asks. The client only sends the block id; the server checks the price, rolls and gives the reward.

-- ServerScriptService/LuckyBlocks.server.luau local ReplicatedStorage = game:GetService("ReplicatedStorage") local LiveConfig = require(ReplicatedStorage.LiveConfig) local openLuckyBlock = ReplicatedStorage:WaitForChild("OpenLuckyBlock") -- a RemoteEvent LiveConfig.replicate() -- let clients show blocks and drop rates openLuckyBlock.OnServerEvent:Connect(function(player, blockId) if typeof(blockId) ~= "string" then return end local block = LiveConfig.get("LuckyBlocks", blockId) if not block then return -- unknown or disabled block end local coins = player.leaderstats.Coins local price = block.price or 0 if coins.Value < price then return end -- Rolls the block's drops list and returns the brainrot it points to. local brainrot = LiveConfig.rollDrop("LuckyBlocks", blockId) if not brainrot then warn(block.name, "has nothing to drop") return end coins.Value -= price giveBrainrot(player, brainrot.id) -- your inventory code: save the id, not the name print(player.Name, "got", brainrot.name, brainrot.rarity, brainrot.income, "/s") end)

rollDrop picks the item’s first list with a chance column (drops here), rolls it, and resolves the row’s reference (brainrot) to the Brainrots item. It returns (brainrot, row). Rows that point at a brainrot that is no longer published (deleted or disabled) are skipped, so a disabled brainrot stops dropping.

Example: show drop percentages

-- StarterPlayerScripts/LuckyBlockInfo.client.luau local ReplicatedStorage = game:GetService("ReplicatedStorage") local LiveConfig = require(ReplicatedStorage.LiveConfig) local function showDrops(blockId: string) local block = LiveConfig.get("LuckyBlocks", blockId) if not block or not block.drops then return end local percents = LiveConfig.chances(block.drops) for index, row in block.drops do local brainrot = LiveConfig.get("Brainrots", row.brainrot) local label = if brainrot then brainrot.name else row.brainrot print(string.format("%s: %.1f%%", label, percents[index])) end end showDrops("golden_block") LiveConfig.onChanged(function() showDrops("golden_block") end, "LuckyBlocks")

Clients

ConfigService is server-only, so clients read a copy:

  1. The server calls LiveConfig.replicate() once.
  2. The SDK mirrors each category’s JSON into ReplicatedStorage.RoControlLiveConfig, a Folder of StringValues named by key, plus the manifest.
  3. On clients, the same API reads that folder, read-only, and onChanged works too.

On a client, the first read waits up to 10 seconds for the folder, then returns empty results with a warning. Always roll drops and check prices on the server: the client copy is for UI.

Live updates

-- Any category LiveConfig.onChanged(function(categoryKey) print(categoryKey, "was updated from RoControl") end) -- One category local connection = LiveConfig.onChanged(function() refreshShop() -- your function end, "Shop") connection:Disconnect() -- when you no longer need it
  • onChanged fires only when a category’s value really changed, including the first time values arrive.
  • Each callback runs in its own thread, so an error in one doesn’t stop the others.
  • New values apply as soon as they arrive. To keep values steady during a round, read them once when the round starts and keep them in a local.

Settings

local multiplier = LiveConfig.value("GameSettings", "coinMultiplier", 1) local luck = LiveConfig.value("GameSettings", "luckBoost", 1) if LiveConfig.value("GameSettings", "eventActive", false) then print(LiveConfig.value("GameSettings", "announcement", "")) end

value returns the default when the value is missing or has a different type than the default.

Loading, errors and Studio

  • The first read on a server loads the configs and can yield briefly. Call LiveConfig.waitUntilReady() at startup to do this wait up front.
  • If configs can’t load, reads return empty tables instead of erroring, a warning is printed once, and the server keeps retrying with backoff. When values arrive, onChanged fires.
  • Returned tables are frozen (table.freeze, deep) so gameplay code can’t change them by accident. They are handed out without copying, which keeps list cheap. Use table.clone to get a copy you can sort.
  • Studio: play sessions see the configs staged in Creator Hub, so you can try values there before publishing. ConfigService:SetTestingValue(key, value) overrides one key on the current server only (in Studio, the current play session), and the SDK picks it up. Clear it with ConfigService:ClearTestingValue(key).

API reference

FunctionReturnsDescription
LiveConfig.list(categoryKey){Item}Items of a collection, in editor order. Empty if unknown
LiveConfig.get(categoryKey, id)Item?One item by id
LiveConfig.settings(categoryKey){[string]: any}The record of a settings category. Empty if unknown
LiveConfig.value(categoryKey, fieldKey, default?)anyOne setting, or default when missing or of another type
LiveConfig.roll(rows, chanceKey?, rng?)Row?Weighted random pick. chanceKey defaults to "chance"; missing or negative chances count as 0; nil when the total is 0. rng is an optional Random
LiveConfig.chances(rows, chanceKey?){number}Percent per row, same order, adding up to 100
LiveConfig.rollDrop(categoryKey, id, listField?, rng?)(Item?, Row?)Rolls an item’s list (the one given, or the first with a chance column) and returns the referenced item and the row
LiveConfig.onChanged(callback, categoryKey?){ Disconnect }Calls callback(categoryKey) when a category changes
LiveConfig.ready()booleanTrue once values have loaded
LiveConfig.waitUntilReady(timeout?)booleanYields until loaded or timeout seconds (default 30)
LiveConfig.replicate()Server only: mirror all categories to clients

Published data

You can also read the configs without the SDK, with ConfigService:GetConfigAsync(). A JSON value can come back as a table or as a JSON string, so decode strings with HttpService:JSONDecode.

{ "LuckyBlocks": { "items": [ { "id": "golden_block", "name": "Golden Block", "image": "rbxassetid://123", "price": 100, "rarity": "Epic", "drops": [ { "brainrot": "tralalero", "chance": 50 }, { "brainrot": "bombardiro", "chance": 5 } ] } ] }, "GameSettings": { "coinMultiplier": 2, "eventActive": true, "announcement": "Double coins!" }, "RoControl": { "v": 1, "categories": { "LuckyBlocks": { "name": "Lucky Blocks", "kind": "collection", "fields": [ { "key": "price", "type": "number" }, { "key": "drops", "type": "list", "chance": "chance", "of": [ { "key": "brainrot", "type": "reference", "ref": "Brainrots" }, { "key": "chance", "type": "number" } ] } ] } } } }

Each top-level key above is a separate Roblox config. The RoControl manifest tells the SDK which list column is the chance and which category a reference points to.

Automate with AI (Claude Code / MCP)

RoControl hosts a remote MCP server, so an AI assistant such as Claude Code can edit Live Config for you. You sign in with your RoControl account in the browser: there is no key to copy.

SettingValue
Server URLhttps://api-rocontrol.duckdns.org/mcp
One game onlyhttps://api-rocontrol.duckdns.org/mcp/GAME_ID
AuthenticationOAuth sign-in (or a personal rcp_… token for scripts)
  1. Add the server to Claude Code:

    claude mcp add --transport http rocontrol https://api-rocontrol.duckdns.org/mcp
  2. In Claude Code, run /mcp, pick rocontrol and choose Authenticate. RoControl opens in your browser: check the account and press Allow.

In the Claude app or on claude.ai, add it under Settings → Connectors → Add custom connector with the same URL.

The connection acts as you, with your workspace permissions, and can’t change your account, password or sign-ins. Disconnect it any time from Live Config → Edit with AI (the robot button in the toolbar).

Games: ids and names

Every game has an ID and a name. list_games shows them and which game is selected; tools work on the selected game unless you name another (by ID, Roblox universe ID or name). Ask Claude to switch (“work on Escape from Granny”) and it calls select_game, which is remembered for that connection, like picking the open place in Studio.

To tie a project folder to one game, run this inside the folder. It is saved in the project’s .mcp.json, so Claude always starts on that game there. The Edit with AI panel shows the command with your game’s ID filled in.

claude mcp add --scope project --transport http rocontrol https://api-rocontrol.duckdns.org/mcp/GAME_ID

Scripts and CI

For automation without a browser, create a personal token in Edit with AI → Scripts and CI and send it as a header:

claude mcp add --transport http rocontrol https://api-rocontrol.duckdns.org/mcp --header "Authorization: Bearer rcp_YOUR_TOKEN"

Tools

ToolDoes
list_gamesList your games with their IDs, names and which one is selected
select_gameChoose the game the other tools work on
list_templatesList the built-in templates
get_live_configRead a game’s categories, items and pending changes
create_categoryCreate a category
update_categoryChange a category: name, key, fields or settings values
delete_categoryDelete a category
upsert_itemCreate an item, or update the one with the same name or id
delete_itemDelete an item
apply_templateAdd a template’s categories to a game
import_from_gameAdd what the game reported (missing categories and items only)
preview_publishShow what would be published and what changed
publishPublish to Roblox

Wherever a tool takes:

  • game (optional), it accepts a game ID, a universe ID or the game’s name;
  • category, it accepts an id, a key or a name;
  • item, it accepts an id, a key or a name.

Example prompts

  • “Add a brainrot called Tung Tung Sahur with income 25/s and put it in the Golden Lucky Block with chance 3, then publish”
  • “In Escape from Granny, list every lucky block with the percent chance of each drop”
  • “Apply the Game settings template, set the coin multiplier to 2, and preview the publish”

REST API

The same API is available over HTTPS at https://api-rocontrol.duckdns.org, with the header Authorization: Bearer rcp_…. :gameId is the RoControl game id from the game page URL (/games/[gameId]); category and item ids come from GET /games/:gameId/live-config.

MethodPathDoes
GET/games/:gameId/live-configCategories, items, pending changes and access status
POST/games/:gameId/live-config/categoriesCreate a category
PATCH/live-config/categories/:idUpdate a category
DELETE/live-config/categories/:idDelete a category
POST/live-config/categories/:id/itemsCreate an item
PATCH/live-config/items/:idUpdate an item
DELETE/live-config/items/:idDelete an item
POST/games/:gameId/live-config/templates/:templateKeyApply a template
GET/games/:gameId/live-config/previewThe JSON that would be published, changed keys and sizes
POST/games/:gameId/live-config/publishPublish. Body: { "message"?, "force"?, "gradual"? }
GET/games/:gameId/live-config/historyRecent publishes
  • Item values are merged: send only the fields you change. A list field is replaced as a whole.
  • publish answers 409 with the conflicting keys when a key was changed outside RoControl. Send "force": true to overwrite. "gradual": true rolls out over about 15 minutes.
TOKEN=rcp_YOUR_TOKEN API=https://api-rocontrol.duckdns.org # Read the game's Live Config curl -H "Authorization: Bearer $TOKEN" "$API/games/GAME_ID/live-config" # Change the Golden Block drops curl -X PATCH "$API/live-config/items/ITEM_ID" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"values":{"drops":[{"brainrot":"tralalero","chance":50},{"brainrot":"bombardiro","chance":8}]}}' # Publish now curl -X POST "$API/games/GAME_ID/live-config/publish" \ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \ -d '{"message":"Golden Block: bombardiro 5 -> 8"}'

Troubleshooting

ProblemFix
Roblox refused the publish (401 or 403)The API key or connection needs universe:read and universe:write for this experience
Image upload refusedAdd the Assets permission (asset:write), or paste an existing image ID
A category is too largeSplit it into two categories (100,000 characters per value)
The game reads empty valuesCheck the output for [LiveConfig] warnings. In Studio, open a place that belongs to the published experience
Clients read empty valuesCall LiveConfig.replicate() in a server Script
An image doesn’t show in an ImageLabelUse the Image ID instead of the Decal ID