Saved Sep 19, 2026 by @enviglo: First version
+ Enviglo's API lets your game, bot or own tools work with your store: record purchases, check who owns what, run codes, read customers and more. This guide gets you from a new key to a working request, and covers what you need to know before building on it.+ ## Before you start++ You need an API key for your store. See [API keys](/wiki/api-keys). Keep it on a server: never in a web page, a LocalScript or a public repository.++ ## The basics++ - **Base URL:** every address starts with `https://enviglo.com/api/v1`. The **API keys** card in **Settings** → **Integrations** shows it too.+ - **Your key:** send it with every request as `Authorization: Bearer YOUR_API_KEY`, or in an `x-api-key` header.+ - **JSON:** answers are JSON. When you send a body, send JSON with `Content-Type: application/json`.+ - **One store:** a key only ever sees the store it belongs to.++ ## Make your first call++ `GET /store` answers with the store your key belongs to and the scopes the key has. Any key can call it, so it's the quickest way to check a key works:++ ```bash+ curl "https://enviglo.com/api/v1/store" \+ -H "Authorization: Bearer YOUR_API_KEY"+ ```++ ```json+ {+ "store": {+ "id": "cls9x0",+ "slug": "your-store",+ "name": "Your Store",+ "badges": ["VERIFIED"],+ "robloxPlaceId": "1234567890",+ "robloxGroupId": "3301",+ "discordGuildId": "998877665544332211",+ "_count": { "products": 12, "licenses": 3410, "followers": 220 },+ "url": "/s/your-store"+ },+ "scopes": ["products:read", "licenses:read", "licenses:write", "discord:read"]+ }+ ```++ `_count` is the store's published products, active licenses and followers.++ The same call from a Roblox server script:++ ```lua+ -- A Script in ServerScriptService. Server only: it holds your API key.+ local HttpService = game:GetService("HttpService")++ local sent, response = pcall(function()+ return HttpService:RequestAsync({+ Url = "https://enviglo.com/api/v1/store",+ Method = "GET",+ Headers = { ["Authorization"] = "Bearer YOUR_API_KEY" },+ })+ end)++ if not sent then+ warn("Couldn't reach Enviglo:", response)+ elseif not response.Success then+ warn("Enviglo answered HTTP " .. response.StatusCode .. ": " .. response.Body)+ else+ local data = HttpService:JSONDecode(response.Body)+ print("This key belongs to " .. data.store.name)+ end+ ```++ Roblox needs **Allow HTTP Requests** switched on for this, under **Game Settings** → **Security** in Studio.++ ## List your products++ With the **Read products** scope, `GET /products` lists your published products, sorted by name, with the Roblox and Discord IDs a game or bot needs:++ ```bash+ curl "https://enviglo.com/api/v1/products" \+ -H "Authorization: Bearer YOUR_API_KEY"+ ```++ ```json+ {+ "products": [+ {+ "id": "clx7a1b2c",+ "slug": "advanced-admin",+ "name": "Advanced Admin",+ "status": "PUBLISHED",+ "licenseType": "SINGLE",+ "robuxPrice": 400,+ "stock": null,+ "robloxProductId": "1234567890",+ "robloxGamePassId": null,+ "discordRoleId": null,+ "activeLicenses": 318,+ "url": "/s/your-store/advanced-admin"+ }+ ]+ }+ ```++ That's trimmed: each product carries more fields. Add `?status=draft`, `?status=archived` or `?status=all` to see other products. A product's `id` is what other endpoints mean by `productId`.++ ## Read errors++ When a request goes wrong, the API answers with a status code and a JSON body saying what happened:++ ```json+ { "error": "This key does not have the licenses:write scope." }+ ```++ | Status | What it means | For example |+ |---|---|---|+ | 400 | Something in the request is wrong: a missing or malformed parameter, or a body that isn't JSON. | "Provide a numeric robloxId." |+ | 401 | The key is missing, mistyped or revoked. | "Invalid or revoked API key." |+ | 403 | The key doesn't have the scope this needs, or the store isn't open. | "This store is not active." |+ | 404 | It isn't in this store. A key only ever sees its own store. | "Product not found in this store." |+ | 409 | It can't happen as things stand, like publishing a product that isn't ready. | "Not ready to publish: …" |+ | 429 | Too many requests. Wait a moment and try again. | "Too many licenses recorded in the last minute. Wait a moment and retry." |++ ## Pages++ Lists that can run long come a page at a time: licenses, codes and requests. Ask for up to 100 at once with `limit`; you get 50 if you don't ask. When there's more, the answer carries `nextCursor`. Send it back as `cursor` for the next page. A `nextCursor` of `null` means you have everything.++ ```bash+ curl "https://enviglo.com/api/v1/licenses?status=active&limit=100" \+ -H "Authorization: Bearer YOUR_API_KEY"++ curl "https://enviglo.com/api/v1/licenses?status=active&limit=100&cursor=clx8f2k0a0001" \+ -H "Authorization: Bearer YOUR_API_KEY"+ ```++ Licenses and codes come newest first, and requests most recently updated first, so each page moves on to older items.++ Customers are paged by number instead: `page=1`, `page=2` and on, with `nextPage` in the answer saying what's next, or `null` when there isn't one.++ ## Rate limits++ Each key can:++ - record 300 purchases a minute with `POST /licenses`, counting repeats and grants;+ - make 60 codes an hour, with `POST /codes`;+ - ask for customer lists and stats 60 times a minute between them, with `GET /customers` and `GET /stats`.++ Past that it answers 429 until the time is up. The lookups a game makes when players join, like checking licenses, have no fixed limit, but ask once per player per server, not on a loop.++ ## Next steps++ - [Sell with developer products](/wiki/sell-with-developer-products): record purchases from your game.+ - [Check licenses in your game](/wiki/check-licenses-in-your-game): unlock what players own.+ - [Webhooks](/wiki/webhooks): hear about changes in your store as they happen.+ - [The API reference](/developers#endpoints) lists every endpoint, with its scope, parameters and an example answer.
Enviglo's API lets your game, bot or own tools work with your store: record purchases, check who owns what, run codes, read customers and more. This guide gets you from a new key to a working request, and covers what you need to know before building on it.
You need an API key for your store. See API keys. Keep it on a server: never in a web page, a LocalScript or a public repository.
https://enviglo.com/api/v1. The API keys card in Settings → Integrations shows it too.Authorization: Bearer YOUR_API_KEY, or in an x-api-key header.Content-Type: application/json.GET /store answers with the store your key belongs to and the scopes the key has. Any key can call it, so it's the quickest way to check a key works:
curl "https://enviglo.com/api/v1/store" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"store": {
"id": "cls9x0",
"slug": "your-store",
"name": "Your Store",
"badges": ["VERIFIED"],
"robloxPlaceId"
_count is the store's published products, active licenses and followers.
The same call from a Roblox server script:
-- A Script in ServerScriptService. Server only: it holds your API key.
local HttpService = game:GetService("HttpService")
local sent, response = pcall(function()
return HttpService:RequestAsync({
Url = "https://enviglo.com/api/v1/store",
Method = "GET",
Headers = { ["Authorization"] = "Bearer YOUR_API_KEY" },
})
end)
if not sent then
warn("Couldn't reach Enviglo:", response)
elseif not response.Success then
warn("Enviglo answered HTTP " .. response.StatusCode .. ": " .. response.Body)
else
local data = HttpService:JSONDecode(response.Body)
print("This key belongs to " .. data.store.name)
end
Roblox needs Allow HTTP Requests switched on for this, under Game Settings → Security in Studio.
With the Read products scope, GET /products lists your published products, sorted by name, with the Roblox and Discord IDs a game or bot needs:
curl "https://enviglo.com/api/v1/products" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"products": [
{
"id": "clx7a1b2c",
"slug": "advanced-admin",
"name": "Advanced Admin",
"status": "PUBLISHED",
"licenseType": "SINGLE",
"robuxPrice": 400,
"stock": null,
"robloxProductId": "1234567890",
"robloxGamePassId": null,
"discordRoleId": null,
"activeLicenses": 318,
"url": "/s/your-store/advanced-admin"
}
]
}
That's trimmed: each product carries more fields. Add ?status=draft, ?status=archived or ?status=all to see other products. A product's id is what other endpoints mean by productId.
When a request goes wrong, the API answers with a status code and a JSON body saying what happened:
{ "error": "This key does not have the licenses:write scope." }
| Status | What it means | For example |
|---|---|---|
| 400 | Something in the request is wrong: a missing or malformed parameter, or a body that isn't JSON. | "Provide a numeric robloxId." |
| 401 | The key is missing, mistyped or revoked. | "Invalid or revoked API key." |
| 403 | The key doesn't have the scope this needs, or the store isn't open. | "This store is not active." |
| 404 | It isn't in this store. A key only ever sees its own store. | "Product not found in this store." |
| 409 | It can't happen as things stand, like publishing a product that isn't ready. | "Not ready to publish: …" |
| 429 | Too many requests. Wait a moment and try again. | "Too many licenses recorded in the last minute. Wait a moment and retry." |
Lists that can run long come a page at a time: licenses, codes and requests. Ask for up to 100 at once with limit; you get 50 if you don't ask. When there's more, the answer carries nextCursor. Send it back as cursor for the next page. A nextCursor of null means you have everything.
curl "https://enviglo.com/api/v1/licenses?status=active&limit=100" \
-H "Authorization: Bearer YOUR_API_KEY"
curl "https://enviglo.com/api/v1/licenses?status=active&limit=100&cursor=clx8f2k0a0001" \
-H "Authorization: Bearer YOUR_API_KEY"
Licenses and codes come newest first, and requests most recently updated first, so each page moves on to older items.
Customers are paged by number instead: page=1, page=2 and on, with nextPage in the answer saying what's next, or null when there isn't one.
Each key can:
POST /licenses, counting repeats and grants;POST /codes;GET /customers and GET /stats.Past that it answers 429 until the time is up. The lookups a game makes when players join, like checking licenses, have no fixed limit, but ask once per player per server, not on a loop.