Docs
Connect your bots
Every bot reports to your dashboard with a small JSON message, called a heartbeat, sent over HTTPS with your connect key. This page covers the flow and the API a client uses.
Getting started
- Create an account and open the Connect tab of your dashboard.
- Generate a connect key and copy it. It is shown once.
- Give the key to each client that should report. All of them can use the same key.
- Start your bots. Each one appears on the dashboard after its first heartbeat.
Connect key
The key looks like ck_… and identifies your account, not a single bot. Anyone holding it can add bots to your dashboard and update their stats, so treat it like a password.
- Only a hash of the key is stored. If you lose it, generate a new one.
- Generating a new key replaces the old one immediately. Bots still using the old key stop reporting until updated.
- Send it as
Authorization: Bearer ck_…on every request.
Heartbeat API
Send a heartbeat every 30 seconds while a bot is active. A bot with no heartbeat for 90 seconds is shown as offline.
POST https://cracked.decint.tools/api/v1/bots/heartbeat
Authorization: Bearer ck_your_key_here
Content-Type: application/json
{
"bot_id": "willow-01",
"name": "Willow-01",
"status": "running",
"script": "Willow Cutter",
"skill": "Woodcutting",
"level": 62,
"world": 301,
"location": "Draynor Village",
"runtime_seconds": 8040,
"xp_gained": 91200,
"xp_per_hour": 41200,
"gp_gained": 27400,
"gp_per_hour": 12300,
"client_version": "0.2.6"
}{ "ok": true, "interval": 30, "bot": { "id": 12, "name": "Willow-01" } }To mark a bot as stopped straight away when a client shuts down, send POST https://cracked.decint.tools/api/v1/bots/stop with {"bot_id": "willow-01"}.
Fields
Only bot_id is required. Fields you leave out keep their last value, so a client can send what changed.
| Field | Type | Notes |
|---|---|---|
bot_id | string | Required. Stable ID for this bot, up to 64 characters of letters, numbers and _ . : @ -. A new ID creates a new bot. |
name | string | Display name, up to 40 characters. Used when the bot is first created and defaults to the bot ID. After that, rename it in the dashboard. |
status | string | running, idle, break, stopped or error. Defaults to running for new bots. |
script | string | Current script, up to 60 characters. |
skill, level | string, number | Main skill being trained and its level. |
world, location | number, string | Game world and a short place name. |
runtime_seconds | number | Seconds the current session has been running. |
xp_gained, xp_per_hour | number | XP this session and the current hourly rate. |
gp_gained, gp_per_hour | number | GP this session and the current hourly rate. |
client_version | string | Client build, up to 24 characters. |
message | string | Short note shown on the bot card, up to 120 characters. Use it for warnings such as "Out of supplies". |
levels | object | Optional map of skill name to level, up to 30 entries. |
Never include game passwords, session tokens or other credentials in a heartbeat. The dashboard only needs stats.
Errors and limits
Errors return a JSON body such as {"error": "invalid_key", "message": "..."}.
| Status | error | Meaning |
|---|---|---|
| 400 | bad_field, missing_field, bad_json | The body was not valid. The message names the field. |
| 401 | missing_key, invalid_key | No key was sent, or it does not match. Generate a new one in the dashboard. |
| 403 | bot_limit_reached | The plan's bot limit is full and this is a new bot ID. Existing bots are unaffected. |
| 413 | too_large | Bodies are limited to 32 KB. |
| 429 | rate_limited | Too many requests. Back off and retry. One heartbeat every 30 seconds per bot is well within limits. |
Examples
curl -X POST https://cracked.decint.tools/api/v1/bots/heartbeat \
-H "Authorization: Bearer ck_your_key_here" \
-H "Content-Type: application/json" \
-d '{"bot_id":"test-1","name":"Test bot","status":"running","script":"Test","xp_per_hour":1000}'import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public final class CrackedReporter {
private static final String ENDPOINT = "https://cracked.decint.tools/api/v1/bots/heartbeat";
private final HttpClient http = HttpClient.newHttpClient();
private final String key;
public CrackedReporter(String connectKey) { this.key = connectKey; }
private static String esc(String s) {
return s.replace("\\", "\\\\").replace("\"", "\\\"").replaceAll("[\\x00-\\x1f]", " ");
}
public void heartbeat(String botId, String name, String status, String script,
long xpPerHour, long xpGained, long gpPerHour, long gpGained,
long runtimeSeconds, int world, String location) throws Exception {
String json = "{\"bot_id\":\"" + esc(botId) + "\",\"name\":\"" + esc(name) + "\","
+ "\"status\":\"" + esc(status) + "\",\"script\":\"" + esc(script) + "\","
+ "\"xp_per_hour\":" + xpPerHour + ",\"xp_gained\":" + xpGained + ","
+ "\"gp_per_hour\":" + gpPerHour + ",\"gp_gained\":" + gpGained + ","
+ "\"runtime_seconds\":" + runtimeSeconds + ",\"world\":" + world + ","
+ "\"location\":\"" + esc(location) + "\"}";
HttpRequest req = HttpRequest.newBuilder(URI.create(ENDPOINT))
.header("Authorization", "Bearer " + key)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
http.sendAsync(req, HttpResponse.BodyHandlers.discarding());
}
}import json, urllib.request
def heartbeat(key, **fields):
req = urllib.request.Request(
"https://cracked.decint.tools/api/v1/bots/heartbeat",
data=json.dumps(fields).encode(),
headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
)
return json.load(urllib.request.urlopen(req, timeout=10))
heartbeat("ck_your_key_here", bot_id="willow-01", status="running", xp_per_hour=41200)