Saved Sep 23, 2026 by @enviglo: Settings tabs, your own Discord bot, custom domains, and giving products by Discord ID or email
- The product's license type must be **One purchase**. Set it under **How buying works** on the product's **Pricing** page.- You need an API key with the **Grant and revoke licenses** (`licenses:write`) scope. See [API keys](/wiki/api-keys).- Your game needs **Allow HTTP Requests** switched on, under **Game Settings** → **Security** in Studio.- - Your game needs a script that records the pass (below). The in-game hub only sells developer products, so it doesn't record passes. If you use the hub, you can add the script to the same place.+ - Your game needs a script that records the pass (below). If you use the in-game hub, you don't: it sells your game passes and records them itself, including passes bought on the Roblox website. See [Set up the in-game hub](/wiki/set-up-the-in-game-hub).## Create the pass and link it…1. On your product's page, they press **Buy on Roblox**. It opens the pass's page on Roblox, where they buy it.2. They join your game. Your script sees they own the pass and records it with Enviglo.- 3. Enviglo puts the license on the Enviglo account their Roblox account is linked to. If they haven't linked one, it waits on a placeholder account and moves to them when they link.+ 3. Enviglo puts the license on the Enviglo account their Roblox account is linked to. If they haven't linked one, it waits for their Roblox account and moves to them when they link.The product page tells buyers the same thing: buy the pass on Roblox, then join your game, and it lands in their library once their Roblox account is linked. There's nothing else to press on Enviglo. Point buyers who haven't linked Roblox to [Link your Roblox account](/wiki/link-your-roblox-account).…
A game pass is bought once, from its own page on Roblox. This guide is for sellers who want to sell a product that way: linking the pass to your product, recording it from your game so the buyer gets their license, and what passes can't do.
licenses:write) scope. See API keys.Enviglo checks the pass with Roblox when you save. If something's wrong, the field says so:
Roblox decides the price. Enviglo reads it from the pass and shows it on your product page, so to change it, change the pass on Roblox.
The product page tells buyers the same thing: buy the pass on Roblox, then join your game, and it lands in their library once their Roblox account is linked. There's nothing else to press on Enviglo. Point buyers who haven't linked Roblox to .
When a player joins, the script asks Roblox whether they own each pass, and records the ones they do with POST /api/v1/licenses. It also records a pass bought while the player is in your game.
Put this in a Script in ServerScriptService. Replace YOUR_API_KEY with your key, and fill in PASSES with each game pass ID and the Enviglo product it gives. The product's Enviglo ID is the id that GET /api/v1/products returns for it.
-- EnvigloGamePasses: a Script in ServerScriptService.
-- It holds your API key, so keep it a server Script. Never a LocalScript, and never in ReplicatedStorage.
local HttpService = game:GetService("HttpService")
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local API_URL = "https://enviglo.com/api/v1"
local API_KEY = "YOUR_API_KEY" -- a key with the licenses:write scope
-- Each game pass, and the Enviglo product it gives: the product's id from GET /api/v1/products.
local PASSES = {
[123456789] = "YOUR_PRODUCT_ID",
}
-- Tells Enviglo this player owns a pass. Safe to repeat: Enviglo records a pass once per player.
local function recordPass(player, productId)
local sent, response = pcall(function()
return HttpService:RequestAsync({
Url = API_URL .. "/licenses",
Method = "POST",
Headers = {
["Authorization"] = "Bearer " .. API_KEY,
["Content-Type"] = "application/json",
},
-- No receiptId: a game pass isn't a receipt.
Body = HttpService:JSONEncode({
robloxId = tostring(player.UserId),
productId = productId,
}),
})
end)
if not sent then
warn("Enviglo: couldn't send the game pass:", response)
elseif not response.Success then
warn("Enviglo: HTTP " .. response.StatusCode .. ": " .. response.Body)
end
end
local function checkPasses(player)
for gamePassId, productId in pairs(PASSES) do
local ok, owns = pcall(function()
return MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamePassId)
end)
if ok and owns then
recordPass(player, productId)
end
end
end
Players.PlayerAdded:Connect(checkPasses)
for _, player in ipairs(Players:GetPlayers()) do
task.spawn(checkPasses, player)
end
-- A pass bought while the player is in this game.
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, gamePassId, wasPurchased)
local productId = PASSES[gamePassId]
if wasPurchased and productId then
recordPass(player, productId)
end
end)
Send no receiptId for a game pass. Enviglo keeps one record per player for each pass, so recording it every time they join is safe:
"created": true, and the purchase shows under Orders → Purchases with the game pass's ID."created": false and the license it already has.A pass is recorded once per player, and registering it again isn't a new purchase. So:
To keep pass licenses with the people who bought them, turn on Block customer transfers for this product on the product's Pricing page. Your team can still move a license.
PASSES is the product's Enviglo ID. A wrong ID answers 404: "Product not found in this store. Pass productId or robloxProductId.""created": false with a revoked license. Your store revoked this player's license. Restore it if they should have it.GET /api/v1/licenses/check first, which has no fixed limit, and only record passes Enviglo doesn't have yet. That needs the Check licenses scope on the key as well.