Your game can ask Enviglo whether a player owns one of your products, and unlock it if they do. This guide is for sellers who script their own games. The check covers everything a player owns from your store, however they got it, as long as it's on the Enviglo account their Roblox account is linked to.
Before you start
You need an API key with the Check licenses (licenses:read) scope. New keys have it ticked. See API keys. The in-game hub's key won't do: it has no API scopes.
Your game needs Allow HTTP Requests switched on, under Game Settings → Security in Studio.
You need the product's Enviglo ID, or its developer product ID. GET /api/v1/products lists both, as id and robloxProductId.
The request
Send a GET to https://enviglo.com/api/v1/licenses/check with your key and these query parameters:
Parameter
What it is
robloxId
Required. The player's Roblox user ID.
productId
The product's Enviglo ID. Send this or robloxProductId.
owned: whether they have it right now. A revoked license or a subscription that has run out doesn't count.
banned: whether you've banned this player, or the Enviglo account they linked. A banned player never owns anything from your store, so check this before offering a purchase.
copies: how many they hold. Every purchase of a Buy more than once product is a license of its own, and this counts them. It's 0 for a banned player.
product: the product's id, name and licenseType. The license types are SINGLE, MULTIPLE and SUBSCRIPTION: One purchase, Buy more than once and Subscription in the dashboard.
license: their live license, or null if they have none. source says how they got it, such as ROBLOX_PURCHASE, GRANT or TRANSFER. expiresAt is when a subscription runs out, and null for everything else.
A server script
This script checks one product for each player who joins, and keeps the answer while they're in the server. Put it in a Script in ServerScriptService, and fill in your key and the product's Enviglo ID.
-- EnvigloOwnership: 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 Players = game:GetService("Players")
local API_URL = "https://enviglo.com/api/v1"local API_KEY = "YOUR_API_KEY"-- a key with the licenses:read scopelocal PRODUCT_ID = "YOUR_PRODUCT_ID"-- the product's id from GET /api/v1/products-- Each player's answer, kept while they're in this server.local answers = {}
-- Asks Enviglo whether a player owns the product. Returns the answer, or nil if it couldn't ask.localfunctioncheckLicense(player)local url = API_URL .. "/licenses/check?robloxId=" .. tostring(player.UserId)
.. "&productId=" .. HttpService:UrlEncode(PRODUCT_ID)
local sent, response = pcall(function()return HttpService:RequestAsync({
Url = url,
Method = "GET",
Headers = { ["Authorization"] = "Bearer " .. API_KEY },
})
end)
ifnot sent then
warn("Enviglo: couldn't check a license:", response)
returnnilendifnot response.Success then
warn("Enviglo: HTTP " .. response.StatusCode .. ": " .. response.Body)
returnnilendlocal decoded, answer = pcall(function()return HttpService:JSONDecode(response.Body)
end)
ifnot decoded thenreturnnilendreturn answer
endlocalfunctiononPlayerAdded(player)local answer = checkLicense(player)
ifnot answer then-- Couldn't ask. That isn't the same as "doesn't own it", so try again later.returnend
answers[player] = answer
if answer.banned thenprint(player.Name .. " is banned from this store")
elseif answer.owned thenprint(player.Name .. " owns " .. answer.product.name .. ", " .. answer.copies .. " held")
if answer.license and answer.license.expiresAt thenprint("Their subscription runs until " .. answer.license.expiresAt)
end-- Unlock the product for them here.else-- Offer it, or remind them to link Roblox on Enviglo if they got it there.print(player.Name .. " doesn't own " .. answer.product.name)
endend
Players.PlayerAdded:Connect(onPlayerAdded)
for _, player inipairs(Players:GetPlayers()) do
task.spawn(onPlayerAdded, player)
end
Players.PlayerRemoving:Connect(function(player)
answers[player] = nilend)
Everything a player owns at once
If you sell several products, one call can fetch them all. GET /api/v1/licenses?robloxId= answers with banned and a licenses list: every license that gives the player access right now, each with its product. A banned player comes back with an empty list. Add this to the script above:
-- Every product a player owns in your store, as a set of Enviglo product ids.-- Returns nil if it couldn't ask. Uses API_URL and API_KEY from the script above.localfunctionownedProducts(player)local sent, response = pcall(function()return HttpService:RequestAsync({
Url = API_URL .. "/licenses?robloxId=" .. tostring(player.UserId),
Method = "GET",
Headers = { ["Authorization"] = "Bearer " .. API_KEY },
})
end)
ifnot sent ornot response.Success thenreturnnilendlocal decoded, data = pcall(function()return HttpService:JSONDecode(response.Body)
end)
ifnot decoded thenreturnnilendlocal owned = {}
for _, license inipairs(data.licenses) do
owned[license.product.id] = trueendreturn owned
end
A product bought more than once appears once for each purchase. The full details are in the API reference.
Players who haven't linked Roblox
Licenses belong to Enviglo accounts, and the check finds them through the account a player's Roblox account is linked to.
They bought it in game. The check finds it even if they've never visited Enviglo. The license waits on a placeholder account that belongs to their Roblox ID, and moves to their own account when they link.
They got it on Enviglo. A free product, a code, or a license your team gave to their username sits on their Enviglo account. Your game can't see it until they link the Roblox account they play on, so owned is false until then.
If a player says they own something your game can't see, ask them to link Roblox in Settings → Connections on Enviglo. Link your Roblox account shows them how. Once they have, the next check finds it.
Caching and limits
The check has no fixed rate limit, but ask once per player per server, not on a loop. Keep the answer while they're in the server, as the script does.
When your ProcessReceipt records a purchase, update the player's saved answer, or check again, so the product unlocks straight away.
If a check fails, don't treat the player as owning nothing. Try again later.
A subscription can run out while someone plays. The next check, when they join again, says so.
Troubleshooting
400 "Provide a numeric robloxId." Send the player's UserId.
404 "Product not found in this store." The ID is wrong, or belongs to another store. A key only sees its own store.
401 "Invalid or revoked API key." Check the key, or make a new one if it was revoked.
403 "This key does not have the licenses:read scope." Make a key with Check licenses.
A player who bought it shows as not owning it. They may have got it on Enviglo and not linked Roblox, or linked a different Roblox account. A subscription may have run out, or the license may have been revoked. Look them up under Customers in your store's dashboard.
You deleted the product. People who own it keep it, and its IDs still work with the check.