API documentation
Everything the dashboard does, it does over this API — so anything you can click, you can automate. Authenticate once with your API key, then manage projects, scripts, keys and your whitelist over plain HTTP and JSON.
1. Base URLs
There are two hosts, and they do different jobs:
| Host | Serves |
|---|---|
| https://solauth.cc | The dashboard API — every endpoint on this page. Called by you, with your API key. |
| https://api.solauth.cc | Loader traffic. Called by your buyers' executors at runtime, never by you. See §8. |
All requests and responses are JSON. Send Content-Type: application/json
on anything with a body.
2. Authentication
Two credentials are involved, and mixing them up is the most common mistake: your API key is 52 characters and identifies your account, and it is exchanged for a short-lived session token that every other endpoint expects. The API key itself is never sent as a bearer token.
Your API key is the account. Treat it like a password — keep it out of your loader, out of your repo and out of anything a buyer can read. If it leaks, rotate it from Account in the dashboard; rotating also kills every session token that was issued from the old key.
Exchange your API key for a session token.
curl -X POST https://solauth.cc/auth/key-login \
-H "Content-Type: application/json" \
-d '{"apiKey":"YOUR_52_CHARACTER_API_KEY"}'{
"token": "eyJhbGciOiJIUzI1NiIs…",
"username": "rayen",
"apiKey": "YOUR_52_CHARACTER_API_KEY",
"apiKeyHint": "…f3a9",
"plan": {
"tier": "pro",
"name": "Pro",
"active": true,
"lifetime": false,
"expiresAt": 1793404800000,
"daysLeft": 24,
"limits": {
"tier": "pro",
"projects": 3,
"scripts": 15,
"scriptsPerProject": 15,
"keys": 500,
"priority": false
}
}
}
A wrong key returns 401 with a deliberately generic message, so
keys can't be probed by comparing responses. Repeated failures from one IP
are throttled and start returning 429.
Send the token as a bearer token on every other request:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs…The account behind the current token, with its plan and limits. Useful as a cheap check that a stored token is still valid before doing real work.
When a token stops working
Tokens are re-checked against the account on every request, not just
at issue, so a token can die before it expires. It stops working when the
account is banned, and when the API key is rotated. Both cases come back as
401: treat any 401 as "log in again", not as a bug.
3. Errors
Every failure is JSON with an error string. Some carry a stable
code as well — prefer matching on that rather than on the message,
which is written for people and may be reworded.
{ "error": "Your PRO plan allows 3 projects. Upgrade to add more.", "code": "PLAN_LIMIT" }| Status | Meaning |
|---|---|
| 400 | The request body is missing something, or asks for two conflicting things at once. |
| 401 | No token, an invalid token, or a token that has been revoked. Log in again. |
| 402 | Blocked by your plan. code is PLAN_REQUIRED (no plan, or it lapsed) or PLAN_LIMIT (over an allowance). |
| 403 | Authenticated, but the resource isn't yours — or the account is suspended. |
| 404 | No such project, script, key or whitelist entry. |
| 429 | Rate-limited, or an action is on cooldown. HWID resets return the cooldown detail with it. |
Nothing is deleted when a plan lapses. A 402 blocks creating
things; reading what you already have keeps working.
4. Projects
A project groups scripts and their keys, and owns the HWID-reset cooldown that applies to its buyers. Deleting one cascades: its scripts and their keys go with it, and that is not reversible.
Your projects, oldest first, each with live counts.
[
{
"id": "kQ2m8xVa1pLz",
"userId": "u_7fd3…",
"name": "Aurora Hub",
"hwidResetCooldownMs": 3600000,
"created_at": 1786579200000,
"scriptCount": 4,
"keyCount": 128,
"execCount": 1284,
"ownerName": "rayen"
}
]Create a project. Returns 201 with the new record.
| Field | Notes |
|---|---|
| name | Required. Trimmed; must not be empty. |
| hwidResetCooldownMs | How long a buyer must wait between HWID resets. Clamped to 10 minutes – 7 days. Defaults to 1 hour. |
Returns 402 if you are at your plan's project allowance, or have
no active plan.
Rename a project or change its cooldown. Send either field or both; anything omitted is left alone.
Delete the project, and every script and key inside it. Returns { "success": true }.
5. Scripts
A script belongs to one project and carries its own access mode. Source is
never returned by the list endpoint — content is stripped from
every row.
Your scripts, oldest first. Pass ?projectId=… to narrow to one
project. Each row carries a keyCount and the ready-to-paste
loader string for that script.
One script, with its loader string. 403 if it isn't yours.
Flip the script between enabled and disabled. This is the kill switch: it takes no body, and it takes effect on the very next execution.
{ "id": "8fq2xk", "disabled": true }
Flip between key required and free access. On a free script the
loader drops the sol_key line. Takes no body.
Restrict the script to a set of places. Send
{ "allowedGames": ["1234567890"] }; an empty array clears the
restriction. The double k is not a typo here — that is the path
the deployed API serves.
Delete the script and every key issued for it.
6. Keys
Two different keys exist and they are not interchangeable — the length is the tell:
- A redeem key is 32 characters. This is what these endpoints mint, and what you sell or hand out. It is redeemed exactly once.
-
A loader key is 16 characters. The buyer gets it when they redeem, and
it is the value that goes in
getgenv().sol_key.
Every key minted for this script, newest first.
Mint one key, or a batch. Returns 201.
| Field | Notes |
|---|---|
| count | How many to mint, 1–500. Defaults to 1. |
| value | Use a specific key value instead of a generated one. Single keys only. |
| note | Free text kept with the key — who it went to, which sale it came from. |
count and value are mutually exclusive; sending both
is a 400. The response shape follows the request: one key comes
back as an object, a batch comes back as
{ "count": n, "keys": [ … ] }.
curl -X POST https://solauth.cc/scripts/8fq2xk/keys \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"count":50,"note":"october batch"}'Revoke a single key. The next execution that presents it is refused — there is no redeploy and no cache to clear.
7. Whitelist
The whitelist maps a Discord user to the projects they may run, and holds the access key their device locks against. You only ever see entries that grant access to your projects.
An object keyed by Discord ID, filtered to your projects.
{
"268473198094811136": {
"discordId": "268473198094811136",
"discordUsername": "rayen",
"note": "bought 12 Oct",
"projectIds": ["kQ2m8xVa1pLz"],
"accessKey": "GtbEFJQgnEfZEHih",
"addedAt": 1786579200000,
"addedBy": "rayen"
}
}
Whether one Discord user has access. Narrow it with ?scriptId=…
or ?projectId=…; without either, it answers globally. Returns
{ "whitelisted": false, "entry": null } when they don't.
Add or update an entry. Returns 201 with the stored record.
| Field | Notes |
|---|---|
| discordId | Required. The buyer's Discord user ID. |
| projectIds | Array of project IDs this entry grants. Every one must be a project you own, or the call is a 403. |
| discordUsername | Stored for display only. |
| note | Free text kept with the entry. |
Posting an existing discordId updates it and keeps the
existing access key, so re-scoping a buyer doesn't break their loader.
Clear the device lock so the buyer's next run re-locks to whatever machine
they're on. Optional body: scriptId picks which project's cooldown
applies, and method is a label written to the audit trail.
Resets are rate-limited by an escalating cooldown. The project's base cooldown applies to the first couple of resets, then multiplies as resets pile up inside a 30-day window, and decays back to normal after a quiet spell. A buyer who changes PC once in a while never notices; a shared key being re-homed repeatedly does.
{ "ok": true, "nextResetAt": 1786582800000, "cooldownMs": 3600000, "resetCount": 1, "escalated": false }{ "error": "Reset on cooldown.", "retryAt": 1786582800000, "retryInMs": 2841000, "cooldownMs": 3600000, "resetCount": 3 }Every reset is logged with the actor, the target and the requesting IP. You can only reset entries scoped to a project you own.
Remove the entry. Their access stops at the next execution.
8. The loader
This is the only part your buyers touch, and the only part that talks to
api.solauth.cc. Copy it from the dashboard — every script's row
carries its own, already filled in.
getgenv().sol_key = "GtbEFJQgnEfZEHih"
loadstring(game:HttpGet("https://api.solauth.cc/loader/8fq2xk"))()
The first line is the buyer's 16-character loader key; the second is the loader
URL for that script. On a script set to free access the first line is dropped
and only the loadstring remains.
The check runs once, at load. Nothing polls and nothing runs per frame, so the cost to your users is a single request while the script starts — not something that follows them through the session.
Everything is decided server-side. A run that fails any check — revoked key, wrong device, disabled script, killed build — never receives the build.
9. Plan limits
The API enforces the same allowances shown on the pricing
section. Going over one returns 402 with
code: "PLAN_LIMIT".
| Tier | Projects | Scripts | Loader keys |
|---|---|---|---|
| basic | 1 | 5 | 250 |
| pro | 3 | 15 | 500 |
| max | 8 | 50 | 1,000 |
Read your own live figures from plan.limits on
/auth/key-login or /auth/me rather than hard-coding
them. A null limit means unlimited — no current tier uses one,
but the field still allows it, so handle it. Security features, the Discord
bot and API access are on every tier — only the allowances differ.
10. Notes & conventions
- All timestamps are milliseconds since the Unix epoch, as numbers.
- IDs are opaque strings. Don't parse them or assume a length.
-
Deletes return
{ "success": true }. Deleting something that was already gone is a404, not a success. -
Only browser navigations to unknown paths are walled off. API calls
from a script or from
curlare unaffected. - There is no API versioning yet. Changes are additive where possible; this page is updated with the API.
Something here not matching what the API actually returns? Tell us in the Discord — a wrong line in this page is treated as a bug in the same way a wrong response would be.
SolAuth