In short
In a Script in ServerScriptService, create a TextChatCommand for each command, set its PrimaryAlias (such as "/kick") and parent it to TextChatService. Its Triggered event gives you the sender's TextSource and the full message. Look up the player with Players:GetPlayerByUserId(textSource.UserId), check their user ID is in your admin list, then split the message into words and act.
- Check the admin list on the server. A check in a LocalScript can be skipped by an exploiter.
- Check user IDs, not names: players can change their names, IDs never change.
- A message that matches a command is not shown in chat, so commands do not spam other players.
Get the script
Admin commands script for TextChatService
One Script in ServerScriptService adds /kick, /tp, /speed, /heal and /kill chat commands that only the user IDs in your admin list can use.
-- Script in ServerScriptService named AdminCommands.
-- Chat commands for the user IDs in ADMINS: /kick, /tp, /speed, /heal, /kill.
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TextChatService = game:GetService("TextChatService")
-- Your user ID is the number in your profile link: roblox.com/users/123456/profile
local ADMINS: { [number]: boolean } = {
[123456] = true,
}
local function isAdmin(userId: number): boolean
-- In a Studio playtest of a place you own yourself, you count as an admin too
local ownerInStudio = RunService:IsStudio() and game.CreatorType == Enum.CreatorType.User and userId == game.CreatorId
return ADMINS[userId] == true or ownerInStudio
end
-- Finds players by the start of their name or display name, or "me", "all", "others"
local function findPlayers(text: string?, caller: Player): { Player }
local found: { Player } = {}
if not text or text == "" or text == "me" then
table.insert(found, caller)
return found
end
local lower = string.lower(text)
for _, player in Players:GetPlayers() do
local name = string.lower(player.Name)
local display = string.lower(player.DisplayName)
if lower == "all"
or (lower == "others" and player ~= caller)
or string.sub(name, 1, #lower) == lower
or string.sub(display, 1, #lower) == lower
then
table.insert(found, player)
end
end
return found
end
local function getHumanoid(player: Player): Humanoid?
local character = player.Character
return character and character:FindFirstChildOfClass("Humanoid")
end
type Handler = (caller: Player, args: { string }) -> ()
local commands: { [string]: Handler } = {
kick = function(caller, args)
for _, target in findPlayers(args[2], caller) do
if target ~= caller then
target:Kick("Kicked by an admin")
end
end
end,
tp = function(caller, args)
-- /tp name: go to them. /tp name1 name2: send name1 to name2.
local destination = if args[3] then findPlayers(args[3], caller)[1] else findPlayers(args[2], caller)[1]
local movers: { Player } = if args[3] then findPlayers(args[2], caller) else { caller }
local destCharacter = destination and destination.Character
if not destCharacter then
return
end
for _, mover in movers do
local character = mover.Character
if character and mover ~= destination then
character:PivotTo(destCharacter:GetPivot() * CFrame.new(0, 0, 4))
end
end
end,
speed = function(caller, args)
-- /speed name 50
local speed = tonumber(args[3]) or 16
for _, target in findPlayers(args[2], caller) do
local humanoid = getHumanoid(target)
if humanoid then
humanoid.WalkSpeed = math.clamp(speed, 0, 200)
end
end
end,
heal = function(caller, args)
for _, target in findPlayers(args[2], caller) do
local humanoid = getHumanoid(target)
if humanoid then
humanoid.Health = humanoid.MaxHealth
end
end
end,
kill = function(caller, args)
for _, target in findPlayers(args[2], caller) do
local humanoid = getHumanoid(target)
if humanoid then
humanoid.Health = 0
end
end
end,
}
-- Creates one TextChatCommand per command. Only admins get a result; for everyone
-- else the command does nothing, and the message is not shown in chat either.
for name, handler in commands do
local command = Instance.new("TextChatCommand")
command.Name = "Admin_" .. name
command.PrimaryAlias = "/" .. name
command.AutocompleteVisible = false -- keep the commands out of everyone's suggestions
command.Parent = TextChatService
command.Triggered:Connect(function(textSource: TextSource, unfilteredText: string)
local caller = Players:GetPlayerByUserId(textSource.UserId)
if not caller or not isAdmin(caller.UserId) then
return
end
handler(caller, string.split(unfilteredText, " "))
end)
end
RoCode builds it in the place you have open in Roblox Studio, on Windows or macOS with a desktop browser; on a phone the button sends the prompt and the link to your computer. Free to start, with free credits every day. More Roblox Studio scripts
Where to put it
ServerScriptService
AdminCommands Script new
Settings you can change
ADMINSIn the script- The user IDs that can use the commands. Replace 123456 with your own ID, and add a line for each admin. Default:
[123456] = true.
Your user ID is the number in your Roblox profile link, for example roblox.com/users/123456/profile. In a Studio playtest of a place you own yourself, you count as an admin even before you add it.
What a cheater can and cannot doEvery command checks the sender's user ID on the server, so a player who is not in ADMINS gets nothing, even with an exploit. Keep the list short: an admin can kick anyone and change anyone's speed.
Type-checked with luau-lsp strict against Roblox's API definitions on 23 October 2026. Not playtested in Studio yet: press Play and watch the Output window before you publish.
Prompt for RoCode
Add admin commands to my game with TextChatService: a Script in ServerScriptService that creates TextChatCommands for /kick, /tp, /speed, /heal and /kill, checks the sender's user ID against an ADMINS list on the server, and accepts a player name, me, all or others as the target.
On this page
How chat commands work
A TextChatCommand inside TextChatService watches messages sent in the chat. When a message starts with its PrimaryAlias or SecondaryAlias, followed by a space or nothing, Roblox does not send the message to other players and fires Triggered instead. The alias match ignores capital letters. Roblox's custom commands example connects to Triggered in a server Script, which is where an admin check belongs.
| Type in chat | What happens |
|---|---|
/kick Name | Removes that player from the server. |
/tp Name | Teleports you to them. /tp Name1 Name2 sends Name1 to Name2. |
/speed Name 50 | Sets their WalkSpeed (0 to 200). No number puts it back to 16. |
/heal Name | Sets their health to full. |
/kill Name | Kills their character. |
Instead of a name you can write me, all or others, and the start of a name is enough: /heal ro heals every player whose name or display name starts with "ro".
Step 1: Add the commands
- Check your game uses TextChatService: select TextChatService in the Explorer and look at ChatVersion in the Properties window. If an old place still says LegacyChatService, Roblox's in-experience text chat page has a section on moving over.
- Insert a Script in ServerScriptService named
AdminCommandsand paste the code below. - Replace
123456inADMINSwith your user ID.
-- Script in ServerScriptService named AdminCommands.
-- Chat commands for the user IDs in ADMINS: /kick, /tp, /speed, /heal, /kill.
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TextChatService = game:GetService("TextChatService")
-- Your user ID is the number in your profile link: roblox.com/users/123456/profile
local ADMINS: { [number]: boolean } = {
[123456] = true,
}
local function isAdmin(userId: number): boolean
-- In a Studio playtest of a place you own yourself, you count as an admin too
local ownerInStudio = RunService:IsStudio() and game.CreatorType == Enum.CreatorType.User and userId == game.CreatorId
return ADMINS[userId] == true or ownerInStudio
end
-- Finds players by the start of their name or display name, or "me", "all", "others"
local function findPlayers(text: string?, caller: Player): { Player }
local found: { Player } = {}
if not text or text == "" or text == "me" then
table.insert(found, caller)
return found
end
local lower = string.lower(text)
for _, player in Players:GetPlayers() do
local name = string.lower(player.Name)
local display = string.lower(player.DisplayName)
if lower == "all"
or (lower == "others" and player ~= caller)
or string.sub(name, 1, #lower) == lower
or string.sub(display, 1, #lower) == lower
then
table.insert(found, player)
end
end
return found
end
local function getHumanoid(player: Player): Humanoid?
local character = player.Character
return character and character:FindFirstChildOfClass("Humanoid")
end
type Handler = (caller: Player, args: { string }) -> ()
local commands: { [string]: Handler } = {
kick = function(caller, args)
for _, target in findPlayers(args[2], caller) do
if target ~= caller then
target:Kick("Kicked by an admin")
end
end
end,
tp = function(caller, args)
-- /tp name: go to them. /tp name1 name2: send name1 to name2.
local destination = if args[3] then findPlayers(args[3], caller)[1] else findPlayers(args[2], caller)[1]
local movers: { Player } = if args[3] then findPlayers(args[2], caller) else { caller }
local destCharacter = destination and destination.Character
if not destCharacter then
return
end
for _, mover in movers do
local character = mover.Character
if character and mover ~= destination then
character:PivotTo(destCharacter:GetPivot() * CFrame.new(0, 0, 4))
end
end
end,
speed = function(caller, args)
-- /speed name 50
local speed = tonumber(args[3]) or 16
for _, target in findPlayers(args[2], caller) do
local humanoid = getHumanoid(target)
if humanoid then
humanoid.WalkSpeed = math.clamp(speed, 0, 200)
end
end
end,
heal = function(caller, args)
for _, target in findPlayers(args[2], caller) do
local humanoid = getHumanoid(target)
if humanoid then
humanoid.Health = humanoid.MaxHealth
end
end
end,
kill = function(caller, args)
for _, target in findPlayers(args[2], caller) do
local humanoid = getHumanoid(target)
if humanoid then
humanoid.Health = 0
end
end
end,
}
-- Creates one TextChatCommand per command. Only admins get a result; for everyone
-- else the command does nothing, and the message is not shown in chat either.
for name, handler in commands do
local command = Instance.new("TextChatCommand")
command.Name = "Admin_" .. name
command.PrimaryAlias = "/" .. name
command.AutocompleteVisible = false -- keep the commands out of everyone's suggestions
command.Parent = TextChatService
command.Triggered:Connect(function(textSource: TextSource, unfilteredText: string)
local caller = Players:GetPlayerByUserId(textSource.UserId)
if not caller or not isAdmin(caller.UserId) then
return
end
handler(caller, string.split(unfilteredText, " "))
end)
end
The commands table maps each name to a function. The loop at the bottom turns every entry into a TextChatCommand, so adding a command is one more entry in the table. string.split(unfilteredText, " ") breaks the message into words: args[1] is the command, args[2] the target, args[3] a number or a second target.
AutocompleteVisible = false keeps the commands out of the suggestions every player sees when they type a slash. They still work when an admin types them in full.
Step 2: Add your own command
Add an entry to the commands table. For example, a /jump Name 100 command that sets jump power:
- Copy the
speedentry and rename itjump. - Inside it, set
humanoid.UseJumpPower = trueandhumanoid.JumpPower = math.clamp(power, 0, 300).
For a message to everyone, such as an announcement, send it through a RemoteEvent that every client shows on screen. Anything players can read that came from typed text has to go through Roblox's text filtering first, so keep announcements to fixed messages, or filter them with TextService:FilterStringAsync.
Test it
- Press Play in a place you own. Type
/speed me 60in chat: you run faster, and the message does not appear in chat. - Type
/heal meafter falling to check health returns. - Start a 2-player test (Test tab, Clients and Servers). From Player1 type
/tp Player2. From Player2, which is not an admin, type/kill Player1: nothing happens.
Common errors and fixes
If a command does nothing, check the Output window and these first.
Typing the command shows it in chat and nothing happens
TextChatCommand belongs to TextChatService. Check TextChatService's ChatVersion property, and that the script is a Script in ServerScriptService with no red error in the Output.
The command does nothing for me in the live game
Your user ID is not in ADMINS. The Studio exception only applies in Studio and only for a place owned by your own account, not a group.
/tp does nothing
Both players need a character in the world. Check the target name: the start of the name or display name is enough, but a typo matches nobody.
I want ranks such as moderator and owner
Change ADMINS to map user IDs to a rank number, and give each command the lowest rank allowed to run it.
Build it with RoCode
If you would rather not set this up by hand, RoCode can build it in the place you have open. RoCode is an independent AI agent for Roblox Studio, not made by Roblox: you describe the change in a web chat, and its Studio plugin creates the scripts in your Explorer.
Add admin commands for me and my friend (user IDs 111 and 222): /kick, /tp, /speed, /heal, /kill and /fly, checked on the server.
- Writes the command script with your admin IDs and any extra commands you describe
- Can read your place first to reuse what is there, such as an existing chat setup
- Is told to compile-check each script with Check Script, and sets undo points around each batch
You still test the commands in a playtest, and with a second player for the admin check. How RoCode connects to Studio.
Questions
How do I add admin commands to my Roblox game?
Create TextChatCommands in a server Script, and in each Triggered handler check the sender's user ID against your admin list before acting. Step 1 has the full script.
Do I need HD Admin or Adonis?
No. Admin systems from the Creator Store add many commands and a panel, but they are large code you did not write. A short script of your own does exactly what you need and nothing else.
How do I find my Roblox user ID?
Open your profile on roblox.com: the number in the link, between /users/ and /profile, is your user ID.
Can players see admin commands in chat?
No. A message that matches a TextChatCommand is not sent to other players.
Sources and further reading
- Roblox Creator Docs: TextChatCommand. aliases, AutocompleteVisible and Triggered
- Roblox Creator Docs: Custom text chat commands. a server Script that handles a command
- Roblox Creator Docs: Player:Kick. removing a player with a message
- Roblox Creator Docs: Text filtering. when typed text must be filtered
How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 23 October 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.