API Reference
Authentication
API tokens, the two header forms, and what a locked token may touch.
Every call carries an API token: a public key that starts with ck_ and a secret that starts with sk_. Create them under API tokens in your panel. The secret is shown once, when you create it, and is stored here only as a hash.
You can hold several tokens at once, so each server, bot or script carries its own and you can revoke one without touching the rest.
Worth knowing
Two header forms
Send
X-Api-Key and X-Api-Secret, or fold them into one Authorization: Bearer ck_key.sk_secret header. They are equivalent.
Workspace tokens
A token created for every application reaches everything in the workspace: licenses on any application, bans, and reseller balances.
Locked tokens
A token locked to one application only sees that application. Keys belonging to another one answer
license_unknown, and the workspace-wide endpoints answer token_scope.
Never ship it
This credential can revoke every license you have issued. It belongs on a server you control, in an environment variable, never inside the build your users install.
Sending the credential
Two headers
X-Api-Key: ck_your_key
X-Api-Secret: sk_your_secret
One bearer
Authorization: Bearer ck_your_key.sk_your_secret
Getting a token
- Open your panelSign in, pick the workspace you want the token to act on, then open API tokens in the sidebar. Only the workspace owner sees it.
- Name it after the callerOne token per server, bot or script. When something leaks, you revoke that one and the rest keep running.
- Choose its reachLeave it on every application for full access, or lock it to one so a compromised token cannot touch the others.
- Copy the secretIt is shown once and stored here only as a hash. Put it in an environment variable, not in your source tree.
Ready to make one?
Tokens live in your panel, under API tokens.
Open API tokens
#Verify a token
GET
https://api.ctyauth.com/manage/v1/ping
Checks the credential and reports the workspace, its plan and how much license room is left. Cheapest way to confirm a token still works.
Can answer
GET
/ping
GET /manage/v1/ping HTTP/1.1
Host: api.ctyauth.com
X-Api-Key: ck_your_key
X-Api-Secret: sk_your_secret
Accept: application/json
curl https://api.ctyauth.com/manage/v1/ping \
-H "X-Api-Key: ck_your_key" \
-H "X-Api-Secret: sk_your_secret"
const response = await fetch('https://api.ctyauth.com/manage/v1/ping', {
method: 'GET',
headers: {
'X-Api-Key': process.env.API_KEY,
'X-Api-Secret': process.env.API_SECRET,
'Accept': 'application/json',
},
});
const data = await response.json();
if (!response.ok) throw new Error(data.error);
$handle = curl_init('https://api.ctyauth.com/manage/v1/ping');
curl_setopt_array($handle, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => [
'X-Api-Key: ' . getenv('API_KEY'),
'X-Api-Secret: ' . getenv('API_SECRET'),
'Accept: application/json',
],
]);
$data = json_decode(curl_exec($handle), true);
import os, requests
response = requests.get(
"https://api.ctyauth.com/manage/v1/ping",
headers={
"X-Api-Key": os.environ["API_KEY"],
"X-Api-Secret": os.environ["API_SECRET"],
},
)
response.raise_for_status()
data = response.json()
{
"seller": {
"name": "My Workspace",
"slug": "my-workspace",
"status": "active",
"plan": "Pro",
"plan_expires_at": "2026-10-14T20:59:59+00:00"
},
"quota": {
"licenses": {
"limit": 5000,
"used": 873,
"left": 4127
}
},
"time": "2026-09-19T09:00:00+00:00"
}
{
"error": "missing_credentials",
"message": "Send your API key and secret."
}
{
"error": "bad_credentials",
"message": "These API credentials are not valid."
}
{
"error": "seller_inactive",
"message": "This account is suspended or its plan has lapsed."
}