Skip to content

Server API

Everything here lives in CoreServerAPI and runs from a server Script:

local hd = game:GetService("ReplicatedStorage"):WaitForChild("HD Admin")
local ServerAPI = require(hd.Core.ServerAPI)

Commands

requestCommand Server

ServerAPI.requestCommand(message: string, fromPlayer: Player?): (boolean, {any}, {Task})

Runs a command message exactly as if it was chatted. When no player is given, a Server User takes the caller's place, so commands can fire from anywhere in your game code without permission checks. When a player is provided, their roles, permissions and rate limits all apply, and any caller notices are sent to them. Returns whether approved, the notices produced, and the tasks it created.

-- Run as the server
ServerAPI.requestCommand(";fly all")

-- Or on behalf of a player
ServerAPI.requestCommand(";fly me", player)

getTasks Server

ServerAPI.getTasks(commandName: string?, target: Target?): {Task}

Returns the running tasks. Both filters are optional. ServerAPI.getTasks() returns every task, ServerAPI.getTasks("fly") every fly task, and ServerAPI.getTasks("fly", player) only that player's fly tasks.


endTasks Server

ServerAPI.endTasks(commandName: string?, target: Target?): number

Ends the matching tasks and returns how many were ended. This is ;unCommand in code form, with the same optional filters as getTasks.

-- End every fly in the server
ServerAPI.endTasks("fly")

-- Clear everything running on one player
ServerAPI.endTasks(nil, player)

canUseCommandAsync Server

ServerAPI.canUseCommandAsync(target: Target, commandName: string): (boolean, string?)

Whether the target's roles allow them to run the given command. Returns true, or false and the reason.


disableCommands Server

ServerAPI.disableCommands(player: Player)

Blocks the player from running any new commands until enableCommands is called or they rejoin. Useful while they're inside a minigame round or a cutscene. Their already-running tasks carry on, so pair this with endTasks if you want those cleared too. It's safe to call straight from PlayerAdded.


enableCommands Server

ServerAPI.enableCommands(player: Player)

Lets the player run commands again.


Roles

Role names are matched case-insensitively, so "VIP" and "vip" reach the same role.

giveRoleAsync Server

ServerAPI.giveRoleAsync(target: Target, roleName: string, options: ActionOptions?): (boolean, string?)

Gives the role to the target and returns true, or false and the reason it couldn't. Permanent grants save, travel cross-server, and work for offline targets too.

Option What it does
scope "perm" saves forever (the default), "server" lasts this server only, "expire" removes itself after duration
duration Seconds, required for scope "expire"
reason The reason for giving
callerUserId A UserId of the person who gave it and to rank-check against
-- Reward a purchase forever
ServerAPI.giveRoleAsync(player, "VIP")

-- A 24 hour trial, granted by an offline head admin
ServerAPI.giveRoleAsync(player, "Mod", {
    scope = "expire",
    duration = 24 * 3600,
    callerUserId = 1234567,
})

takeRoleAsync Server

ServerAPI.takeRoleAsync(target: Target, roleName: string, options: ActionOptions?): (boolean, string?)

Takes the role from the target, including permanent grants made on other servers or while they were offline. options accepts scope ("perm" or "server") and callerUserId, matching giveRoleAsync.


getRolesAsync Server

ServerAPI.getRolesAsync(target: Target): (boolean, {Role} | string)

Returns true and the target's roles ordered highest rank first, or false and a reason. The target must be in this server.

local success, roles = ServerAPI.getRolesAsync(player)
if success then
    for _, role in roles do
        print(role.name, role.rank)
    end
end

getTopRoleAsync Server

ServerAPI.getTopRoleAsync(target: Target): (boolean, (Role | string)?)

Returns true and the target's most senior role (nil if they hold none), or false and a reason. The target must be in this server.


hasRoleAsync Server

ServerAPI.hasRoleAsync(target: Target, roleName: string): boolean

Whether the target currently holds the role. The target must be in this server.


getRole Server

ServerAPI.getRole(roleName: string): Role?

Returns the Role for the given name, or nil if no role matches.


getAllRoles Server

ServerAPI.getAllRoles(): {Role}

Returns every role in the game's configuration.


Moderation

banAsync Server

ServerAPI.banAsync(target: Target, options: BanOptions): (boolean, string?)

Bans the target for entering your game, and kicks them if they're currently in any server. Returns true and the new banId, or false and the reason it couldn't. Offline targets work too.

Option What it does
reason Required, shown to the player when they try to join
scope "perm" (the default), "server" for this server only, or "expire"
duration Seconds, for scope "expire"
expiresAt An absolute epoch which overrides duration
banAlts Defaults to true, banning alt accounts through Roblox's BanService
callerUserId A UserId to credit the ban to and rank-check against. Leave it out for a system ban with no restrictions
-- A 7 day ban issued by your own anti-cheat
ServerAPI.banAsync(player, {
    reason = "Exploiting",
    scope = "expire",
    duration = 7 * 24 * 3600,
})

unbanAsync Server

ServerAPI.unbanAsync(target: Target, banId: string?, options: UnbanOptions?): (boolean, string?, number?)

Clears the target's bans and returns (success, failReason, totalBansCleared). Pass a banId to clear one specific ban, or nil to clear them all. options accepts scope and callerUserId.


getBanAsync Server

ServerAPI.getBanAsync(target: Target): (BanRecord?, string?)

Returns the target's active BanRecord and its banId, checking offline records too, or nil if they aren't banned.

local ban, banId = ServerAPI.getBanAsync(userId)
if ban then
    print(`Banned for '{ban.reason}' by {ban.bannedBy}`)
end

Prompts

prompt Shared

ServerAPI.prompt(promptType: PromptType, player: Player, text: string, options: PromptOptions?): PromptHandle

Shows the given prompt using HD Admin's own UI. promptType is one of:

Prompt type What it shows
"info" "success" "warn" "error" Sidebar notices in the matching style
"message" A centre-screen message
"hint" A bar across the top of the screen
"privateMessage" A full-screen message with a reply prompt
"alert" A centre card that demands attention
"vote" A poll the player can answer, with "voteResults" presenting the outcome
"action" A card with buttons and controls you define

Common options include title, duration, color, icon, openPage (clicking the notice opens that menu page) and dismissable. The vote and action types take a lot more than this, which autocomplete shows you in full.

The handle it returns lets you dismiss the prompt early with handle:Disconnect().

ServerAPI.prompt("success", player, "Quest complete!", {duration = 5})

Info

getSetting Shared

ServerAPI.getSetting(settingName: string, player: Player?): any

Returns the value of a game setting, like ServerAPI.getSetting("Prefix"). Pass a player to also apply their own You Settings, so you get the exact value that player experiences.


getVersion Shared

ServerAPI.getVersion(): string

The running HD Admin version, like "v2.0.0".


Emotes

getEmoteById Server

ServerAPI.getEmoteById(emoteId: number): Emote?

Returns the registered Emote with the given id, or nil.


getEmoteByName Server

ServerAPI.getEmoteByName(emoteName: string, matchLength: boolean?): Emote?

Returns the registered Emote matching the given name, or nil.


getRandomEmote Server

ServerAPI.getRandomEmote(): Emote?

addEmoteAsync Server

ServerAPI.addEmoteAsync(emote: IncompleteEmote, dontRegister: boolean?): (boolean, Emote | string)

Registers a new emote from an emote animation asset, so it can be played by ;emote and appear in the emotes menu. Only emoteId is required, and the missing details are fetched for you. Returns true and the completed Emote, or false and the reason it couldn't.


Types

Role

The full role configuration table, exactly as defined under ConfigRoles. See Roles.


BanRecord

type BanRecord = {
    type: "perm" | "expire" | "server",
    source: "admin" | "loader",
    bannedBy: number | string,
    bannedAt: number,
    expiresAt: number?,
    grantedOnJobId: string?, -- only present on server-scope bans
    reason: string,
    banAlts: boolean,
}

The banId isn't part of the record itself, so getBanAsync hands it to you as a second return value.


Task

See Task API.


Emote

type Emote = {
    name: string,
    originalName: string?,
    order: number,
    animationId: number,
    emoteId: number,
}