Luau SDK quickstart
A server-side Luau module. Drop it in, call Init with a server key, and presence, doors, moderation commands and clock-ins start working without you writing a request.
Last updated 10 August 2026
Overview
The SDK is a client for the game REST API. It has no privileged access — everything it does you could do yourself with HttpService. What it saves you is the tedious and easy-to-get-wrong part: batching events inside the request budget, backing off correctly, keeping a bounded local queue, acking commands, caching door and position lookups, and flushing on shutdown.
It runs on the game server. Nothing in it should be required from a LocalScript, and your server key must never reach a client.
Install
Three ways in, in order of how much tooling you already have.
Wally
Roblox model
Take the published model from the workspace game settings page and drag it into ServerScriptService. The model is versioned; the version you insert is the version you keep until you replace it deliberately.
One-file drop-in
A single ModuleScript with no dependencies, for people who do not use a package manager and do not want a folder of files. Same API, same behaviour, shipped alongside every release.
However you install it, the module ends up at a path you require from a server script.
Enable HttpService
To turn it on: Studio, then Game Settings, then Security, then Allow HTTP Requests.
The SDK detects this case specifically and logs an explicit message naming the setting, rather than passing along a 403 that looks identical to a scope problem. If you see the message below, the key is fine and the toggle is not.
- The toggle is per experience, so a test place and a live place need it separately.
- It does not persist across a fresh place created from a template. Check it after any migration.
- A 403 with HttpService already on means a scope problem on the server key instead. Check the key’s scopes.
Initialise
One call, once, from a server script at boot. Everything else is optional.
After Init, the SDK registers the instance, starts the heartbeat loop, tracks joins and leaves on its own, and fetches the door manifest. You do not need to report presence manually.
The five calls
TrackEvent
Queue an event. It does not make a request — it goes into the local queue and rides the next heartbeat. The event id is generated at the moment you call this, which is what makes a retried heartbeat safe.
The type is a free string. join, leave, team_change, death and spawn are understood by the analytics layer; anything else is stored as a custom type for your workspace. Do not put personal data in the payload — user ids and usernames only.
CanOpenDoor
A synchronous access decision. Served from cache in the normal case, because the full door set for a user is prefetched on join.
Both grants and denials are written to the access log with a reason. The denial log is what tells an operator their policy is wrong, so do not suppress calls you expect to fail.
CommandReceived
An event that fires for each remote command that arrives on a heartbeat response. The SDK acks a command once your handler returns without erroring, and the ack rides the next heartbeat.
Handle the types your game supports and ignore the rest. An unhandled type is still acked, because from the moderator’s side a command that silently sat in a queue is worse than one that is reported as done-and-ignored. If you need to signal genuine failure, error out of the handler and the command is marked failed rather than acked.
GetPosition
The position a user holds in the organization, from the cache the heartbeat maintains. Use it for in-game titles, callsigns, spawn logic and anything else that should follow the org chart rather than a Roblox rank.
It returns nil for a user who has not linked their account or is not a member. Treat nil as “civilian”, not as an error.
Init
Covered above. It is the only call that is mandatory.
What the SDK handles
None of this is magic and all of it is in the API reference. It is here so you do not have to write it.
When we are unreachable
Your game must keep working when GROUPSPACE does not answer. The SDK degrades on purpose rather than throwing.
- Door decisions fall back to the door’s configured fail mode: closed for restricted doors, open for public ones. That choice is made per door in GROUPSPACE, not in your code.
- A cached decision is used past its TTL rather than denying, while the SDK is in a failure state. A stale allow is usually better than locking your whole player base out of a public building.
- Events keep queueing up to the cap and ship when the connection returns.
- Commands simply do not arrive. There is nothing to handle — the moderator sees the command expire rather than believing it landed.
GetPositionserves the last known value and returns nil for users who joined during the outage.
Versions and pinning
The SDK does not auto-update, and that is a decision rather than an omission. A surprise behaviour change in a live server with 200 people in it is worse than being one release behind.
- Pin an exact version in Wally, or keep the model you inserted. Nothing rewrites it for you.
- The version you are running is reported as
sdkVersionon every heartbeat and shown per instance in the live server view, so an operator can see which servers are stale. - Upgrades are a deliberate step: bump the pin, test in your test environment, publish.
- Register a separate game with
environment: testand its own key rather than pointing a test place at live data.
Not on Roblox
There is one SDK and it is Roblox-only. Steam, FiveM, Minecraft, a Unity or Source server, or a script on a box somewhere all integrate by calling the REST API directly. That is not a lesser path — the HTTP API is the contract and the SDK is a client for it.
Nothing in the game surface is Roblox-shaped. externalServerId is a Roblox job id, a Steam server id or a UUID your game generates at boot, and GROUPSPACE does not care which.
