API Reference
Bans
Block a device or an address across every application in the workspace.
A ban refuses a device id or an IP address on every application you own, whatever license it presents. Running sessions on the banned value are cut at the moment you create it.
Worth knowing
Workspace tokens only
Bans span the whole workspace, so a token locked to one application is refused here with
token_scope.
The message is shown as it is
Whatever you put in
message reaches the user verbatim. Leave it out and they get a neutral refusal that never says what was banned.
#List bans
GET
https://api.ctyauth.com/manage/v1/bans
Newest first.
Needs a workspace token. A token locked to one application is refused with
token_scope.
Query parameters
typestringoptionalhwidorip.qstringoptional- Matches part of the banned value. Up to 128 characters.
per_pageintegeroptional- Between 1 and 200. Defaults to 50.
Can answer
GET
/bans
GET /manage/v1/bans 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/bans \
-H "X-Api-Key: ck_your_key" \
-H "X-Api-Secret: sk_your_secret"
const response = await fetch('https://api.ctyauth.com/manage/v1/bans', {
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/bans');
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/bans",
headers={
"X-Api-Key": os.environ["API_KEY"],
"X-Api-Secret": os.environ["API_SECRET"],
},
)
response.raise_for_status()
data = response.json()
{
"bans": [
{
"id": 12,
"type": "ip",
"value": "203.0.113.9",
"note": "chargeback",
"message": "Contact support.",
"hits": 0,
"last_hit_at": null,
"created_at": "2026-09-19T09:00:00+00:00"
}
],
"page": {
"current": 1,
"last": 1,
"total": 3,
"per_page": 50
}
}
{
"error": "missing_credentials",
"message": "Send your API key and secret."
}
{
"error": "bad_credentials",
"message": "These API credentials are not valid."
}
{
"error": "token_scope",
"message": "This token is locked to one application. Use a workspace token for this endpoint."
}
{
"error": "bans_unavailable",
"message": "Bans need the latest database update on this panel."
}
{
"error": "validation_failed",
"message": "The duration field is required.",
"errors": {
"duration": [
"The duration field is required."
]
}
}
#Ban a value
POST
https://api.ctyauth.com/manage/v1/bans
Answers with the ban and how many live sessions it ended.
Needs a workspace token. A token locked to one application is refused with
token_scope.
Body
valuestringrequired- The device id or IP address. Up to 190 characters.
typestringoptionalhwidorip. Left out, it is worked out from the value.notestringoptional- Private reason, only ever seen in your panel. Up to 500 characters.
messagestringoptional- Shown to the banned user. Up to 160 characters.
Can answer
POST
/bans
POST /manage/v1/bans HTTP/1.1
Host: api.ctyauth.com
X-Api-Key: ck_your_key
X-Api-Secret: sk_your_secret
Accept: application/json
Content-Type: application/json
{
"value": "203.0.113.9",
"note": "chargeback",
"message": "Contact support."
}
curl https://api.ctyauth.com/manage/v1/bans \
-X POST \
-H "X-Api-Key: ck_your_key" \
-H "X-Api-Secret: sk_your_secret" \
-H "Content-Type: application/json" \
-d '{"value":"203.0.113.9","note":"chargeback","message":"Contact support."}'
const response = await fetch('https://api.ctyauth.com/manage/v1/bans', {
method: 'POST',
headers: {
'X-Api-Key': process.env.API_KEY,
'X-Api-Secret': process.env.API_SECRET,
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ value: '203.0.113.9', note: 'chargeback', message: 'Contact support.' }),
});
const data = await response.json();
if (!response.ok) throw new Error(data.error);
$handle = curl_init('https://api.ctyauth.com/manage/v1/bans');
curl_setopt_array($handle, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'X-Api-Key: ' . getenv('API_KEY'),
'X-Api-Secret: ' . getenv('API_SECRET'),
'Accept: application/json',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['value' => '203.0.113.9', 'note' => 'chargeback', 'message' => 'Contact support.']),
]);
$data = json_decode(curl_exec($handle), true);
import os, requests
response = requests.post(
"https://api.ctyauth.com/manage/v1/bans",
headers={
"X-Api-Key": os.environ["API_KEY"],
"X-Api-Secret": os.environ["API_SECRET"],
},
json={"value": "203.0.113.9", "note": "chargeback", "message": "Contact support."},
)
response.raise_for_status()
data = response.json()
{
"ban": {
"id": 12,
"type": "ip",
"value": "203.0.113.9",
"note": "chargeback",
"message": "Contact support.",
"hits": 0,
"last_hit_at": null,
"created_at": "2026-09-19T09:00:00+00:00"
},
"sessions_ended": 2
}
{
"error": "missing_credentials",
"message": "Send your API key and secret."
}
{
"error": "bad_credentials",
"message": "These API credentials are not valid."
}
{
"error": "token_scope",
"message": "This token is locked to one application. Use a workspace token for this endpoint."
}
{
"error": "ban_exists",
"message": "That value is already banned."
}
{
"error": "bans_unavailable",
"message": "Bans need the latest database update on this panel."
}
{
"error": "validation_failed",
"message": "The duration field is required.",
"errors": {
"duration": [
"The duration field is required."
]
}
}
#Lift a ban
DELETE
https://api.ctyauth.com/manage/v1/bans/{id}
The value works again immediately.
Needs a workspace token. A token locked to one application is refused with
token_scope.
Path parameters
idintegerrequired- The ban id.
Can answer
DELETE
/bans/{id}
DELETE /manage/v1/bans/12 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/bans/12 \
-X DELETE \
-H "X-Api-Key: ck_your_key" \
-H "X-Api-Secret: sk_your_secret"
const response = await fetch('https://api.ctyauth.com/manage/v1/bans/12', {
method: 'DELETE',
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/bans/12');
curl_setopt_array($handle, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
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.delete(
"https://api.ctyauth.com/manage/v1/bans/12",
headers={
"X-Api-Key": os.environ["API_KEY"],
"X-Api-Secret": os.environ["API_SECRET"],
},
)
response.raise_for_status()
data = response.json()
{
"deleted": 12
}
{
"error": "missing_credentials",
"message": "Send your API key and secret."
}
{
"error": "bad_credentials",
"message": "These API credentials are not valid."
}
{
"error": "token_scope",
"message": "This token is locked to one application. Use a workspace token for this endpoint."
}
{
"error": "ban_unknown",
"message": "No ban with that id."
}
{
"error": "bans_unavailable",
"message": "Bans need the latest database update on this panel."
}