Hello developers
Roblox has introduced an updated Communities Role Management system and new Cloud API endpoints related to it, which makes managing group roles a lot easier. One endpoint that really stood out to me is the Update Group Membership endpoint, which lets developers rank users directly through the API without needing to create external services.
Before this a lot of developers had to host ranking bots outside of Roblox. This module is designed to make that process simpler and provide a solution for ranking users using the new system.
This module is mainly designed for work roleplay and military games that require automated ranking systems to manage rank progression. For example, many roleplay groups use application center games where users complete quizzes to earn roles. When there are large numbers of applicants, manually ranking each user will be time consuming, so automated systems are commonly used to handle this process.
The goal of this module is to make transitioning from older systems as smooth as possible. Its core function follows the familiar structure of most games’ external ranking services, which typically involve the group ID, user ID, and rank. This module allows you to directly use the Roblox API without having to manually handle role IDs, membership IDs, etc..
To use this module you need to:
-
Have HTTP Requests enabled in your experience
-
Have a Roblox Cloud API key (it needs to have
group:readandgroup:writepermissions, and you need to have permission to rank users in said group.) -
Store your API key in a Secret
It’s recommended to name the secret “APIKey”, since that’s the default name used in the module. However, the secret name can be changed, as it’s an editable variable in the module.
Note: To create a secret, go to your game’s Creator Dashboard and open the Secrets tab.
To set it up:
-
Add the module to ServerScriptService
-
Change any variables you’d like to change, these are the two editable variables and the line they are on:
3 local DefaultGroupId = 12345 -- Set to your main group
4 local SecretName = "APIKey" -- Set to your secret's name that you created
The module has the following functions:
Rank Module Functions
Keep in mind the GroupId variable is optional if you set the DefaultGroupId variable.
RankUser(UserId, TargetRank, GroupId) Ranks a user to specified rank within the group.
RankUserByName(UserId, RoleName, GroupId) Ranks a user using the role name instead of the rank number.
Promote(UserId, GroupId) Promotes a user to the next role based on the group’s role hierarchy.
Demote(UserId GroupId) Demotes a user to the previous role based on the group’s role hierarchy.
All functions have the same return format
success : boolean, result : string
This module gets roles dynamically and handles pagination internally. It works reliably even in large groups or groups with non-sequential ranks.
You can find the module here.
If you have any feedback, suggestions or run into any issues, feel free to reply below.
PS: This the first time i publish a model of any sort here. Hopefully I did well.
Module Source Code
local HttpService = game:GetService("HttpService")
local DefaultGroupId = 12345 -- Set to your main group
local SecretName = "APIKey" -- Set to your secret's name that you created.
-- Setting the DefaultGroupId variable will make the GroupID argument optional
-- in all Ranker functions.
local ApiUrls = {
GetRoles = "https://apis.roblox.com/cloud/v2/groups/%s/roles?maxPageSize=20",
GetUserMembership = "https://apis.roblox.com/cloud/v2/groups/%s/memberships?filter=user=='users/%s'"
}
local Ranker = {}
local function GetRoles(GroupId : string | number)
local Url = string.format(ApiUrls.GetRoles, GroupId)
local Headers = {
["x-api-key"] = HttpService:GetSecret(SecretName)
}
local Roles = {}
local NextPageToken = nil
repeat
local FinalUrl = Url
if NextPageToken then
FinalUrl ..= "&pageToken=" .. NextPageToken
end
local Success, Response = pcall(function()
return HttpService:RequestAsync({
Url = FinalUrl,
Method = "GET",
Headers = Headers
})
end)
if not Success or not Response.Success then
warn("[AutoRanker] Failed to fetch roles:", Response)
return nil
end
local Data = HttpService:JSONDecode(Response.Body)
for _, Role in Data.groupRoles do
table.insert(Roles, Role)
end
NextPageToken = Data.nextPageToken
until NextPageToken == ""
table.sort(Roles, function(a, b)
return a.rank < b.rank
end)
return Roles
end
local function GetRoleId(GroupId : string | number, Rank : number)
local Roles = GetRoles(GroupId)
if not Roles then return nil end
for _, Role in Roles do
if Role.rank == Rank then
return Role.id
end
end
return nil
end
local function GetRoleByName(GroupId : string | number, RoleName : string)
local Roles = GetRoles(GroupId)
if not Roles then return nil end
for _, Role in Roles do
if string.lower(Role.displayName) == string.lower(RoleName) then
return Role
end
end
return nil
end
local function GetMembershipInfo(GroupId : string | number, UserId : string | number)
local Url = string.format(ApiUrls.GetUserMembership, GroupId, UserId)
local Headers = {
["x-api-key"] = HttpService:GetSecret(SecretName)
}
local Success, Response = pcall(function()
return HttpService:RequestAsync({
Url = Url,
Method = "GET",
Headers = Headers
})
end)
if not Success or not Response.Success then
warn("[AutoRanker] Failed to fetch membership:", Response)
return nil
end
return HttpService:JSONDecode(Response.Body)
end
local function GetUserRole(GroupId, UserId)
local Data = GetMembershipInfo(GroupId, UserId)
if not Data or not Data.groupMemberships or not Data.groupMemberships[1] then
return nil
end
local RolePath = Data.groupMemberships[1].role
local RoleId = string.match(RolePath, "roles/(%d+)")
local Roles = GetRoles(GroupId)
if not Roles then return nil end
for _, Role in Roles do
if tostring(Role.id) == RoleId then
return Role
end
end
return nil
end
function Ranker.RankUser(UserId : number, TargetRank : number, GroupId : number?)
GroupId = GroupId or DefaultGroupId
local PathData = GetMembershipInfo(GroupId,UserId)
if not PathData or not PathData.groupMemberships or not PathData.groupMemberships[1] then
warn("[AutoRanker] User not found in the group.")
return false, "User not found in the group."
end
local Path = PathData.groupMemberships[1].path
local RoleId = GetRoleId(GroupId,TargetRank)
if not RoleId then
warn("[AutoRanker] Role not found.")
return false, "Role not found in the group."
end
local Url = string.format("https://apis.roblox.com/cloud/v2/%s", Path)
local Body = {
path = Path,
role = string.format("groups/%s/roles/%s",GroupId, RoleId),
user = string.format("users/%s",UserId)
}
local Headers = {
["Content-Type"] = "application/json",
["x-api-key"] = HttpService:GetSecret(SecretName)
}
local Success, Response = pcall(function()
return HttpService:RequestAsync({
Url = Url,
Method = "PATCH",
Headers = Headers,
Body = HttpService:JSONEncode(Body)
})
end)
if not Success then
warn("[AutoRanker] Request failed:", Response)
return false, Response.Body or Response
end
if Response.Success then
return true, Response.Body
else
warn("[AutoRanker] API Error:", Response.StatusCode, Response.Body)
return false, Response.Body or Response
end
end
function Ranker.RankUserByRoleName(UserId : number, RoleName : string, GroupId : number?)
GroupId = GroupId or DefaultGroupId
local Role = GetRoleByName(GroupId, RoleName)
if not Role then
return false, "Role not found"
end
return Ranker.RankUser(UserId, Role.rank, GroupId)
end
function Ranker.Promote(UserId : number, GroupId : number?)
GroupId = GroupId or DefaultGroupId
local Roles = GetRoles(GroupId)
if not Roles then
return false, "Failed to fetch roles"
end
local CurrentRole = GetUserRole(GroupId, UserId)
if not CurrentRole then
return false, "User role not found"
end
for i, Role in ipairs(Roles) do
if Role.id == CurrentRole.id then
local NextRole = Roles[i + 1]
if not NextRole then
return false, "Already at highest rank"
end
return Ranker.RankUser(UserId, NextRole.rank, GroupId)
end
end
return false, "Role not found in role list"
end
function Ranker.Demote(UserId : number, GroupId : number?)
GroupId = GroupId or DefaultGroupId
local Roles = GetRoles(GroupId)
if not Roles then
return false, "Failed to fetch roles"
end
local CurrentRole = GetUserRole(GroupId, UserId)
if not CurrentRole then
return false, "User role not found"
end
for i, Role in ipairs(Roles) do
if Role.id == CurrentRole.id then
local PrevRole = Roles[i - 1]
if not PrevRole then
return false, "Already at lowest rank"
end
return Ranker.RankUser(UserId, PrevRole.rank, GroupId)
end
end
return false, "Role not found in role list"
end
return Ranker
Example Code
local Ranker = require(path.to.module)
local success, result = Ranker.RankUserByRoleName(1027658271, "Admin", 12345678)
if success == true then
print("Sucessfully ranked!")
else
print("Ranking failed!" , result)
end
Do you find this module useful for your use case?
- Yes
- No