Passes on Roblox: how to create them, how to sell them, and the date that changes cross-game sales
A pass charges once for a permanent privilege. This guide covers the right order to create one, the price limits, why the privilege is not automatic, and what the official documentation marked for 30 May 2026.
Produced with AI from the sources cited below, and checked by automated verification against those same sources. No person read this text before publication. Editorial Policy

If your game sells a pass that is used in another game of yours — or if you build bundles that reach across your catalogue — there is a date in the official Roblox documentation you need to have looked at: 30 May 2026. It is the day cross-game pass sales were marked to be disabled.
That date has already passed. This piece explains what a pass is, how to create one properly, what the documentation says about cross-game sales, and how to check, in code, whether any flow of yours still depends on it.
The problem
The confusion starts before the date. A pass and a developer product are different things, and choosing wrong costs you a whole shop's worth of rework.
According to the documentation, a pass exists to charge the user a one-time Robux fee for a privilege inside the game: entry to a restricted area, an in-game avatar item, a permanent power-up. For anything a player might buy several times — a potion, a temporary power-up, in-game currency — the page itself tells you to use a developer product.
The practical rule is repetition, not price: if the same player might want it twice, it is not a pass.
The second problem is sequence. Before creating a pass, the game must already be published and accessible on Roblox. Anyone trying to set up monetization before publishing gets stuck at the very first step, and the message that appears does not make that obvious.
And the third is what prompted this piece: cross-game sales. If your architecture assumes a pass bought in game A unlocks something in game B, the documentation recommends migrating to Robux transfers before the marked date, precisely so there is no service interruption.
Step by step
The official route to creating a pass has a fixed order:
- Publish the game first. Without that the pass does not exist as an option.
- Go to Creations and select the game.
- Go to Monetization, then Passes.
- Click Create pass.
- Upload the icon image. The documentation is specific: it must not exceed 512x512 pixels, must not have important detail outside the circular boundaries, and must be in
.jpg,.pngor.bmp. - Write a name and a description.
- Choose the category — it is what organises the item in the in-game shop.
- Click Create pass again, to confirm.
The circle detail is what catches unprepared people most: the image is uploaded square and displayed cropped. Text pushed into the corner disappears.
Once created, you need the pass ID for anything in script. It comes from Monetization ⟩ Passes: hover over the thumbnail, click the ellipsis button and choose Copy Asset ID.
There are two more rules worth checking before you publish the sale:
- If the pass is used as a randomized reward, the documentation tells you to review the randomized virtual item policy first.
- If you use price optimization, the script has to run on the client, so each user sees their own personalised price.
That second point is easy to get wrong silently: a server script fetching the price shows the same number to everyone, and you will not find out by looking at your own game.
Selling outside the game
You can also sell the pass on the Store tab of the game page, and that route is different: in Monetization ⟩ Passes, hover over the pass, open the ellipsis menu, select the pass, go to Sales and switch on the Item for Sale toggle. Then fill in the price in Robux and save.
The price has defined limits: the minimum is 1 Robux and the maximum is 1 billion Robux. And the value you type directly affects how much Robux you receive per sale — it is not a decorative number.
The privilege is not automatic
This is the part that most surprises anyone building their first shop: buying the pass gives the player nothing by itself. The documentation is direct — you have to manually assign the pass privileges to whoever bought it.
The recommended route is to use PlayerAdded when the player joins the game, check whether they already own the pass, and then grant whatever the pass promises. And there is an instruction about location: the script lives in ServerScriptService, so that the server handles privileges — not the client.
For the moment of purchase there is the PromptGamePassPurchaseFinished event, which tells you when the flow finished and whether it succeeded. That is where you grant the privilege to someone who has just bought, without waiting for them to leave and come back.
The history is your problem
A detail the documentation declares and almost nobody reads: Roblox does not record per-user pass purchase history. You can request a download of the sales data, but if you want to know who bought what and when, keeping that is your responsibility.
Anyone who finds this out late usually finds out at the worst moment: when a player complains they paid and received nothing, and there is nowhere to get the answer from.
The artifact
The script below does two things: it reads the pass information through the official route and it shows the price. It exists so you can confirm, in your own game, that the ID is right and the pass is on sale — before tying any logic to it.
Two points the documentation marks and the script respects: the call is GetProductInfo, and the second parameter for a pass must be Enum.InfoType.GamePass. Swapping that for another type returns data about something else, or nothing at all.
The pcall is not fussiness: the call goes to the network and can fail for reasons that have nothing to do with your code.
Common mistakes
The most expensive mistake here is not a syntax one — it is architectural, and it only shows up once it is already in production.
Assuming the pass crosses games. If your game A sells a pass that game B checks, that is exactly the design the documentation marked for disabling on 30 May 2026, recommending Robux transfers before the date to avoid interruption. If you built that and never went back to look, the moment to check is now, not when a player opens a ticket.
The second mistake is confusing the item type. A pass sold where there should be a developer product does not break: it simply caps the revenue, because the player who would buy five times buys once. That shows up in no error log — it shows up on the revenue chart, months later, as a number that never grows.
The third is price optimization running on the server. The displayed price is the same for everyone, the personalisation does not happen, and because the game works, nobody investigates.
And the fourth is the icon: uploading rectangular art with the pass name written along the edge. The circular crop cuts the text, and what is left in the shop is an image with no identification.
There is a fifth, and it is the one that generates support tickets: putting the privilege grant on the client instead of the server. The documentation asks for the script in ServerScriptService precisely because whoever decides what the player earned cannot be the player's own machine. It works in testing, and it vanishes with the first player who reloads the page.
The sixth is keeping nothing. Since per-user history does not exist on the Roblox side, a game with no records of its own has no way to answer "did this player buy?" six months later — and the default answer becomes handing the benefit over out of politeness, which is an expensive policy.
Next step
If you have published passes, run this sweep, in this order:
- List the passes another of your games checks. Any ownership check made outside the game that sold the pass goes on the list.
- For each one, decide the replacement. The documentation's own recommendation is Robux transfers.
- Check the type of each item. A pass for a one-time purchase, a developer product for anything bought again.
- Run the script above with each ID. It answers in a single run whether the ID is right and whether the item is on sale.
If you are still going to create your first pass, start in the right order: publish the game, create the pass, copy the ID, and only then write the logic. The temptation to write the script first is strong, and the missing ID is what loses you the afternoon.
For anything bought more than once, the developer products page is the place to continue — and the decision between the two formats, made before you build the shop, is the one that saves you rebuilding it afterwards.
Sources
- Passes | Documentation - Roblox Creator Hub — checked on
Ready-to-use artifact
-- ServerScriptService: the server decides what the player earned, never the client.
-- Covers both moments: whoever ALREADY had the pass on joining, and whoever buys now.
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local PASS_ID = 0000000 -- swap in your own pass ID (Copy Asset ID)
local function grantPrivilege(player)
-- put here whatever the pass promises: speed, an area, an item.
-- this is the ONLY place that grants, so there are never two truths.
local humanoid = player.Character and player.Character:FindFirstChildOfClass("Humanoid")
if humanoid then
humanoid.WalkSpeed = 32
end
end
local function playerOwnsThePass(player)
local ok, owns = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, PASS_ID)
end)
if not ok then
-- a network call fails for reasons that are not your code.
-- denying in silence is worse than saying so: log it and carry on.
warn("failed to check the pass of " .. player.Name .. ": " .. tostring(owns))
return false
end
return owns
end
Players.PlayerAdded:Connect(function(player)
if playerOwnsThePass(player) then
grantPrivilege(player)
end
player.CharacterAdded:Connect(function()
-- the character respawns and the privilege goes with it: reapply.
if playerOwnsThePass(player) then
grantPrivilege(player)
end
end)
end)
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, passId, bought)
if bought and passId == PASS_ID then
grantPrivilege(player)
-- per-user purchase history does NOT exist on the Roblox side:
-- if you need to know who bought, record it here.
end
end)


