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

  1. Create an account and open the Connect tab of your dashboard.
  2. Generate a connect key and copy it. It is shown once.
  3. Give the key to each client that should report. All of them can use the same key.
  4. 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.

Request
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"
}
Response
{ "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.

FieldTypeNotes
bot_idstringRequired. Stable ID for this bot, up to 64 characters of letters, numbers and _ . : @ -. A new ID creates a new bot.
namestringDisplay 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.
statusstringrunning, idle, break, stopped or error. Defaults to running for new bots.
scriptstringCurrent script, up to 60 characters.
skill, levelstring, numberMain skill being trained and its level.
world, locationnumber, stringGame world and a short place name.
runtime_secondsnumberSeconds the current session has been running.
xp_gained, xp_per_hournumberXP this session and the current hourly rate.
gp_gained, gp_per_hournumberGP this session and the current hourly rate.
client_versionstringClient build, up to 24 characters.
messagestringShort note shown on the bot card, up to 120 characters. Use it for warnings such as "Out of supplies".
levelsobjectOptional 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": "..."}.

StatuserrorMeaning
400bad_field, missing_field, bad_jsonThe body was not valid. The message names the field.
401missing_key, invalid_keyNo key was sent, or it does not match. Generate a new one in the dashboard.
403bot_limit_reachedThe plan's bot limit is full and this is a new bot ID. Existing bots are unaffected.
413too_largeBodies are limited to 32 KB.
429rate_limitedToo many requests. Back off and retry. One heartbeat every 30 seconds per bot is well within limits.

Examples

curl
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}'
Java 11+ (no dependencies)
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());
    }
}
Python 3 (standard library)
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)