Skip to content

Server

The server-side data API: bundle.Server. Index it with a Player to get that player's typed accessor tree (Data[player].Coins.Increment(50)), and call its methods to drive persistence, monetization, leaderboards, and administration.

Data[player] and Data.Get error while a profile is still loading. Use Server.WaitForData first (or read inside a command handler, which only runs once the player is Ready).

Indexing

Data[player] returns the same accessor tree as Data.Get(player). The methods on individual fields (.Get, .Set, .Increment, …) are documented on the Value class.

Lifecycle

.WaitForData

serveryields
Server.WaitForData(player: Player)  (Value?, string?)

Yields until the player's profile is Ready, then returns their accessor tree. On failure returns (nil, reason) where reason is "load-failed", "timeout", or "session-end". Always handle the nil branch. This is the safe way to get data in Players.PlayerAdded.

.GetState

server
Server.GetState(player: Player)  "Loading" | "Ready" | "SessionEnded"

The player's session state, without yielding.

.Get

server
Server.Get(player: Player)  Value

The player's accessor tree. Errors if the profile is not Ready. Prefer Server.WaitForData on join. Data[player] is shorthand for this.

.Batch

server
Server.Batch(player: Player, fn: () -> ())

Runs fn as a batch: all writes inside coalesce into a single replication flush and one Changed pass. Use for bulk updates.

.Transaction

server
Server.Transaction(player: Player, fn: () -> ())  (boolean, string?)

Runs fn atomically: if it throws, every write inside is rolled back and (false, error) is returned. On success returns (true, nil).

Persistence

.Flush

serveryields
Server.Flush(player: Player, opts: { Force: boolean? }?)  boolean

Forces a save now instead of waiting for the next autosave. Call it right after a grant or purchase. Force = true also pushes through a blocked wipe-guard save.

.GetSaveInfo

server
Server.GetSaveInfo(player: Player)  SaveInfo

The player's save state ({ LastSaveAt, LastResult, Dirty }) for "Saving… / Saved ✓ / Unsaved changes" UI. Also mirrored to the owner.

.GetOffline

serveryields
Server.GetOffline(userId: number)  { [string]: any }?

Reads a profile that is not on this server (offline, or another server), or nil if it doesn't exist. Read-only. Use Server.UpdateOffline to write.

.UpdateOffline

serveryields
Server.UpdateOffline(userId: number, fn: (data: { [string]: any }) -> ())  (boolean, string?)

Mutates an offline profile's raw data. Fails closed if the user has an active session anywhere, so it can never clobber a live game.

.ListVersions

serveryields
Server.ListVersions(userId: number, limit: number?)  { { VersionId: string, CreatedAt: number, Size: number? } }

Lists a profile's DataStore version history, for support/rollback tools.

.GetVersion

serveryields
Server.GetVersion(userId: number, versionId: string)  { [string]: any }?

Reads the raw data of a specific historical version.

.RestoreVersion

serveryields
Server.RestoreVersion(userId: number, versionId: string)  (boolean, string?)

Restores a profile to a historical version. Fails closed if the user is active on another server.

.Erase

serveryields
Server.Erase(userId: number)  (boolean, string?)

GDPR right-to-erasure: removes the profile and the user's leaderboard entries. Returns (false, reason) if any part failed so you can retry.

.Export

serveryields
Server.Export(userId: number)  string?

GDPR data export: the profile as a JSON string (buffers base64-encoded), or nil if it doesn't exist.

Commands

.Command

server
Server.Command(name: string, specOrHandler: ({ Args: { string }? } | (player: Player, ...any) -> ...any), handler: ((player: Player, ...any) -> ...any)?)

Registers a named command clients can call via Client.Request. Sender identity is always the real player; args are shape-validated, rate-limited, and only run once the caller is Ready.

Data.Command("EquipItem", { Args = { "string" } }, function(player, itemId)
    if not Data[player].Inventory[itemId].Get() then return false, "not owned" end
    Data[player].Equipped.Set(itemId)
    return true
end)

Leaderboards

.GetLeaderboard

server
Server.GetLeaderboard(name: string, limit: number?)  { LeaderboardEntry }

The cached top-N of an all-time board ({ Rank, UserId, Name, Score }). Never triggers a live OrderedDataStore read.

.GetMyRank

server
Server.GetMyRank(player: Player, name: string)  number?

The player's current rank on a board, or nil if unranked.

Monetization

.PromptGift

server
Server.PromptGift(buyer: Player, productName: string, recipientUserId: number)  (boolean, string?)

Prompts buyer to gift a product's perk to recipientUserId. Records a durable intent before money moves; delivery survives cross-server hops and offline recipients, and a held gift credit is consumed with no charge. Returns (false, reason) if the gift can't proceed.

.GetGiftCredits

server
Server.GetGiftCredits(player: Player)  { [string]: number }

The player's unassigned gift credits by product name.

.HandleReceipt

server
Server.HandleReceipt(receiptInfo: table)  Enum.ProductPurchaseDecision

Processes a developer-product receipt. Scribe binds ProcessReceipt automatically unless OwnReceipts = false, in which case call this from your own handler. Idempotent by PurchaseId and fail-closed. Robux are never eaten.

.Owns

server
Server.Owns(player: Player, key: string)  boolean

The unified ownership check: a granted perk or an owned game pass (cached) or RobloxPlus. Hide "buy" buttons whenever this is true.

.GrantPerk

server
Server.GrantPerk(player: Player, key: string)

Grants a perk directly (admin/reward flows).

.RevokePerk

server
Server.RevokePerk(player: Player, key: string)

Revokes a granted perk.

.Purchase

server
Server.Purchase(player: Player, spec: PurchaseSpec)  (boolean, string?)

Atomic soft-currency purchase: debits Cost, runs Grant, and logs it. All or nothing. Insufficient funds or a throwing Grant rolls everything back with no debit.

Data.Purchase(player, {
    Cost = { Path = "Coins", Amount = 45000 },
    Category = "Vehicle", ItemId = "Police01",
    Grant = function(data) data.Vehicles.Insert("Police01") end,
})

.RecordPurchase

server
Server.RecordPurchase(player: Player, entry: table)

Appends an entry to the player's in-game purchase log (your own records).

.GetPurchases

server
Server.GetPurchases(player: Player, filter: { Kind: ("Robux" | "InGame")?, Category: string?, ItemId: string?, Since: number?, Limit: number? }?) → { table }

The player's purchase history (Robux receipts and in-game purchases), newest first, optionally filtered.

Cooldowns

.OnCooldown

server
Server.OnCooldown(player: Player, key: string, seconds: number)  (onCooldown: boolean, remaining: number)

Checks and arms a keyed cooldown: returns false and starts a new seconds cooldown if it was off. Call it only at the grant/claim moment. Use Server.PeekCooldown for display checks.

.PeekCooldown

server
Server.PeekCooldown(player: Player, key: string)  (onCooldown: boolean, remaining: number)

Read-only cooldown check. Never arms it. Safe for UI.

.ClearCooldown

server
Server.ClearCooldown(player: Player, key: string)

Clears a cooldown (support/testing resets).

Signals

.OnSave

signalserver
Server.OnSave: Signal

Fires after every save attempt with { Player, Ok, Duration, At }.

.SessionEnded

signalserver
Server.SessionEnded: Signal

Fires when a session ends with (player, reason). With KickOnSessionEnd = true (the default) the player is also kicked.

.OnAnomaly

signalserver
Server.OnAnomaly: Signal

Fires for integrity anomalies (out-of-bounds writes, a tripped wipe guard, an unserializable snapshot) with { Player, Path, Value, Reason }.

.OnGiftReceived

signalserver
Server.OnGiftReceived: Signal

Fires when a player receives a gift with (player, { FromUserId, Product, GiftId }).

.OnGiftCredit

signalserver
Server.OnGiftCredit: Signal

Fires when a player is issued a gift credit with (player, productName).

Messaging

.OnMessage

signalserver
Server.OnMessage: Signal

Fires with (player, message) when a cross-server message sent through Server.SendMessage arrives for an active session.

.SendMessage

serveryields
Server.SendMessage(userId: number, message: any)  boolean

Sends a durable cross-server message to userId, delivered to Server.OnMessage on whatever server they are (or next become) active on, including offline recipients (queued until their next load). message must be DataStore-serializable. Returns whether it was committed. Rides ProfileStore's global-update channel, so keep messages small and infrequent.

Escape hatch

.ProfileStore

propertyserver
Server.ProfileStore: ProfileStore

The underlying ProfileStore instance, an advanced escape hatch for store-level operations Scribe doesn't wrap (version reads, raw MessageAsync, and so on). It bypasses Scribe's schema, replication, and session guarantees, so treat it as read-mostly and never mutate an active-session profile through it. Most games never need this; prefer the typed API and Server.SendMessage.

.Raw

propertyserver
Server.Raw: Server

The same Data object without the typed accessor tree: the untyped escape hatch if the type solver ever trips on your template.