Key check
The one call your mod makes: send the key and the device id, get back your response template.
No API key needed: call it straight from your mod. Field names and the response are set per software in Connect Settings; the defaults are shown here.
Keep in mind
- Check
status, not HTTP - After HTTP 200, check the status field in the response body.
- Time runs from the first check
- A time-limited key starts its timer on the first successful validation, not when issued.
- Each check takes a slot
- A successful request takes a device slot if one is available.
- Single-use keys
- A single-use key (single) is deleted immediately after successful binding.
Check a key
https://dev.mod.wtf/{soft_id}/connect
Send the key and the device id as a form body. A key that passes binds the device and gets your success template back. Form-urlencoded only, Accept: application/json, body up to 4096 bytes.
Form fields
-
gamestring requiredGame or product. Up to 32 characters.
-
user_keystring requiredLicense key. Up to 32 characters. Pattern
^[A-Za-z0-9:@_-]{1,32}$. -
serialstring requiredDevice identifier (HWID). Up to 255 characters. No commas, quotes or control characters.
POST /{soft_id}/connect HTTP/1.1
Host: dev.mod.wtf
Content-Type: application/x-www-form-urlencoded
Accept: application/json
game=pubg&user_key=XXXX-XXXX-XXXX-XXXX&serial=device-id
curl -sS -X POST 'https://dev.mod.wtf/{soft_id}/connect' \
-H 'Accept: application/json' \
--data-urlencode 'game=pubg' \
--data-urlencode 'user_key=XXXX-XXXX-XXXX-XXXX' \
--data-urlencode 'serial=device-id'
const response = await fetch("https://dev.mod.wtf/{soft_id}/connect", {
method: "POST",
headers: { Accept: "application/json" },
body: new URLSearchParams({
"game": "pubg",
"user_key": "XXXX-XXXX-XXXX-XXXX",
"serial": "device-id"
}),
});
const result = await response.json();
$ch = curl_init('https://dev.mod.wtf/{soft_id}/connect');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Accept: application/json'],
CURLOPT_POSTFIELDS => http_build_query([
'game' => 'pubg',
'user_key' => 'XXXX-XXXX-XXXX-XXXX',
'serial' => 'device-id',
]),
]);
$result = json_decode(curl_exec($ch), true);
import requests
response = requests.post(
"https://dev.mod.wtf/{soft_id}/connect",
data={
"game": "pubg",
"user_key": "XXXX-XXXX-XXXX-XXXX",
"serial": "device-id",
},
timeout=10,
)
result = response.json()
#include <curl/curl.h>
#include <string>
static size_t collect(char* data, size_t size, size_t count, void* out) {
static_cast<std::string*>(out)->append(data, size * count);
return size * count;
}
int main() {
CURL* curl = curl_easy_init();
std::string response;
std::string body = "game=pubg&user_key=XXXX-XXXX-XXXX-XXXX&serial=device-id";
curl_easy_setopt(curl, CURLOPT_URL, "https://dev.mod.wtf/{soft_id}/connect");
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, collect);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
{
"status": true,
"data": {
"real": "pubg-XXXX-XXXX-XXXX-XXXX-device-id-<CONNECT_SECRET>",
"token": "<SIGNATURE_HEX>",
"modname": "",
"mod_status": "",
"credit": "",
"ESP": "",
"Item": "",
"AIM": "",
"SilentAim": "",
"BulletTrack": "",
"Floating": "",
"Memory": "",
"Setting": "",
"EXP": "2026-11-05 13:25:42",
"Enc": "List Of Games",
"device": 1,
"rng": 1793269542
},
"info": "powered by mdk panel",
"domain": "https://dev.mod.wtf/"
}
{
"status": false,
"reason": "Key not found!"
}
{
"status": false,
"reason": "Request too large"
}
{
"status": false,
"reason": "Request limit exceeded. Try again in a minute. >MDK<"
}
License errors
A refusal is status: false with a text in reason, rendered through your error template. Only status and reason reach the mod: check status and show reason. The log code is written to the Connect audit log and is never sent.
| Default reason | When | HTTP | Log code |
|---|---|---|---|
Request limit exceeded. Try again in a minute. >MDK< |
Too many checks from one address for this software. | 429 |
rate_limit |
Servers is off |
The platform is in maintenance. Its maintenance text replaces this default when one is set. | 200 |
maintenance |
Paste key! |
A field is missing or too long: key up to 32 characters, device id 255, game 32, custom fields 255. | 200 |
invalid_request |
Invalid serial |
The device id is empty or holds a comma, a double quote or a control character. | 200 |
invalid_serial |
Invalid key |
The key holds a character other than letters, digits and : @ _ -. |
200 |
invalid_key |
Request limit exceeded. Try again in a minute. >MDK< |
Too many checks from one device id. | 429 |
rate_limit |
Enter key! |
The key is 0. |
200 |
invalid_key |
Serial banned |
The device id is banned for this software. | 200 |
banned_serial |
Paste data! |
The game field is on and the game is empty, or the device id is 0. |
200 |
authentication_failed |
Soft is banned! |
The platform has banned the software. | 200 |
soft_disabled |
{status_reason} |
The software is paused; its pause reason is sent as the text. Admin keys still pass. | 200 |
soft_disabled |
Key not found! |
No such key for this software and game. Set by err_key_not_found. |
200 |
key_not_found |
Key is on pause! |
The key is paused. Set by err_key_on_pause. |
200 |
key_paused |
Key has been blocked! |
The key is blocked. Set by err_key_blocked. |
200 |
key_blocked |
Key has expired! |
The key has expired. Set by err_expire_key. |
200 |
key_expired |
Device limit reached! |
A new device and every slot of the key is taken. Set by err_device_max_slots. |
200 |
device_limit |
Another device is already linked from this IP address. |
One HWID per IP is on and another device already checked this key from the same address. The text is set in Connect Settings. | 200 | — |
Key texts can be changed per software with the err_* fields of edit-soft or in Connect Settings; Test Connect uses its own test_err_* texts.
HTTP statuses
| HTTP | When |
|---|---|
| 200 | Validation result, including normal license denials |
| 404 | Endpoint unavailable |
| 413 | Request body exceeds 4096 bytes |
| 429 | Request rate limit exceeded |
| 500 | Could not generate a response |
| 503 | Temporary server failure; retry later |
Device binding
- First check binds
- The key is bound to the HWID on the first successful validation.
- Known devices come back
- A device already bound to the key can check again at any time without taking a new slot.
- One HWID per IP
- Optional and off by default. When on, a second device from the same address is refused; unlimited keys skip this check.
- Banned devices
- A banned device id is refused for every key of the software with
banned_serial.
Response template
The success and error bodies are JSON templates. Any string can hold these placeholders; ModKey fills them in before answering.
| Placeholder | Value | Default path |
|---|---|---|
{{real}} |
The signature source built for this request |
/data/real |
{{token}} |
The response token, lowercase hex |
/data/token |
{{expires_at}} |
Key expiry, Y-m-d H:i:s |
/data/EXP |
{{max_devices}} |
Device limit of the key |
/data/device |
{{timestamp}} |
Unix time of the answer |
/data/rng |
{{encoding}} |
Always List Of Games |
/data/Enc |
{{info}} |
Always powered by mdk panel |
/info |
{{origin}} |
Scheme and host that answered |
/domain |
{{game}} |
Game sent in the request | — |
{{key}} |
License key sent in the request | — |
{{serial}} |
Device id sent in the request | — |
{{soft_id}} |
Software id | — |
{{soft_name}} |
Software name | — |
{{reason}} |
Refusal text, error template only |
/reason |
{{modname}} |
Online System value |
/data/modname |
{{mod_status}} |
Online System value |
/data/mod_status |
{{credit}} |
Online System value |
/data/credit |
{{esp}} |
Online System value |
/data/ESP |
{{item}} |
Online System value |
/data/Item |
{{aim}} |
Online System value |
/data/AIM |
{{silent_aim}} |
Online System value |
/data/SilentAim |
{{bullet_track}} |
Online System value |
/data/BulletTrack |
{{floating}} |
Online System value |
/data/Floating |
{{memory}} |
Online System value |
/data/Memory |
{{setting}} |
Online System value |
/data/Setting |
Custom domains
Point your own domain at ModKey and the same check answers on https://keys.example.com/connect and /test/connect, up to 5 domains per endpoint. Add domains in the panel or through API v2.