Skip to content

Detections and replays

All paths are relative to https://api.thorium.ac/api/v1 and need Authorization: Bearer thor_.... See the overview for paging, rate limits and errors.

A detection is one incident: one check firing for one player, on one server, in one session. If the check keeps firing in that session, the same detection’s count goes up rather than a new one being created. Detections are returned within your plan’s history window.

The detection object

{
"id": "9a1d0a0e-2f3b-5c4d-8e6f-0123456789ab",
"alertType": "MCSpeed",
"count": 3,
"acknowledged": false,
"acknowledgedReason": "",
"createdAt": "2026-09-28T17:50:02.114Z",
"updatedAt": "2026-09-28T17:51:40.902Z",
"sessionId": "e41b6c7d-0a92-4f3e-8d15-6b2c9f0e7a31",
"playerId": "0f6e2c1a-8b4d-4e57-9a3c-2d1f7b6e5a90",
"playerUsername": "Notch",
"playerSteamId": "",
"playerUuid": "069a79f4-44e9-4726-a5be-fca90e38aaf5",
"serverId": "11111111-2222-4333-8444-555555555555",
"serverName": "Survival"
}
FieldMeaning
idDetection id. Also the replay id and the alert_id in webhooks.
alertTypeThe check, e.g. MCSpeed, MCMultiAura. See detections.
countHow many times the check fired in this incident.
acknowledgedtrue once reviewed.
acknowledgedReasonThe reviewer’s note, empty until reviewed.
createdAtFirst time the check fired.
updatedAtLast time it fired or was reviewed; null if neither has happened since creation.
sessionIdThe player session it happened in.
playerUsername, serverName"Unknown" if the player or server no longer exists.

List detections

GET /players/paginated/detections · scope ViewAlerts

Newest activity first by default. Dismissed detections are not returned.

QueryDefaultMeaning
orgIdrequiredNetwork id
page, pageSize1, 50pageSize max 100
searchCheck name (substring), username (word match) or exact Minecraft UUID
checkallExact check name, e.g. MCSpeed
statusallopen (not reviewed) or reviewed
serverIdallOnly this server
sinceOnly detections created at or after this time (ISO 8601)
sortlastFlaglastFlag, check, player, server, flags or status
dirdescasc or desc
Terminal window
curl -s -H "Authorization: Bearer $THORIUM_API_KEY" \
"https://api.thorium.ac/api/v1/players/paginated/detections?orgId=$ORG_ID&status=open&since=2026-09-28T00:00:00Z&pageSize=1"
{
"detections": [
{
"id": "9a1d0a0e-2f3b-5c4d-8e6f-0123456789ab",
"alertType": "MCSpeed",
"count": 3,
"acknowledged": false,
"acknowledgedReason": "",
"createdAt": "2026-09-28T17:50:02.114Z",
"updatedAt": "2026-09-28T17:51:40.902Z",
"sessionId": "e41b6c7d-0a92-4f3e-8d15-6b2c9f0e7a31",
"playerId": "0f6e2c1a-8b4d-4e57-9a3c-2d1f7b6e5a90",
"playerUsername": "Notch",
"playerSteamId": "",
"playerUuid": "069a79f4-44e9-4726-a5be-fca90e38aaf5",
"serverId": "11111111-2222-4333-8444-555555555555",
"serverName": "Survival"
}
],
"history": {
"days": 90,
"retentionDays": 120,
"since": "2026-06-30T18:04:05.000Z"
},
"pagination": {
"currentPage": 1,
"pageSize": 1,
"totalDetections": 27,
"totalPages": 27,
"hasNextPage": true,
"hasPreviousPage": false
}
}

To poll for new detections, keep the newest createdAt you have seen and pass it as since next time, or use webhooks instead of polling.

Detection stats

GET /players/alerts/stats · scope ViewAlerts

Counts over the last days days (default 7, max 365, and never further back than your plan’s history window).

Terminal window
curl -s -H "Authorization: Bearer $THORIUM_API_KEY" \
"https://api.thorium.ac/api/v1/players/alerts/stats?orgId=$ORG_ID&days=3"
{
"total": 58,
"acknowledged": 21,
"dismissed": 4,
"byType": [
{ "type": "MCSpeed", "count": 19 },
{ "type": "MCMultiAura", "count": 12 },
{ "type": "MCReach", "count": 9 }
],
"byDay": [
{ "date": "2026-09-26", "count": 17 },
{ "date": "2026-09-27", "count": 22 },
{ "date": "2026-09-28", "count": 15 }
]
}

total includes dismissed detections; byType and byDay do not. Days are UTC.

Mark a detection reviewed

PATCH /players/alerts/{alertId}/acknowledge · scope ManageDetections

Marks the detection as reviewed with a note. It stays in lists and counts, with acknowledged: true.

Terminal window
curl -s -X PATCH \
-H "Authorization: Bearer $THORIUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "Checked replay, player banned"}' \
"https://api.thorium.ac/api/v1/players/alerts/9a1d0a0e-2f3b-5c4d-8e6f-0123456789ab/acknowledge?orgId=$ORG_ID"
{
"id": "9a1d0a0e-2f3b-5c4d-8e6f-0123456789ab",
"alertType": "MCSpeed",
"count": 3,
"acknowledged": true,
"acknowledgedReason": "Checked replay, player banned",
"createdAt": "2026-09-28T17:50:02.114Z",
"updatedAt": "2026-09-28T18:12:44.301Z",
"playerId": "0f6e2c1a-8b4d-4e57-9a3c-2d1f7b6e5a90",
"serverId": "11111111-2222-4333-8444-555555555555",
"sessionId": "e41b6c7d-0a92-4f3e-8d15-6b2c9f0e7a31",
"details": "{\"reason\":\"speed\",\"obs\":0.412,\"lim\":0.351,\"mods\":[\"eff:speed:1\"],\"ticks\":6,\"ping\":85}"
}

details is the check’s evidence as a JSON-encoded string. Every alert has reason. Checks that measure one value against one limit add obs (what the player did) and lim (the limit on this server, before tolerance). mods lists what moved that limit off vanilla, such as eff:speed:1 (Speed II), attr:entity_interaction_range:4 or gm:1 (creative). Other keys vary by check.

400 Reason is required. without a reason, 404 Alert not found. if the id is not on this network or was dismissed.

Dismiss a detection

PATCH /players/alerts/{alertId}/dismiss · scope ManageDetections

Marks the detection as a false positive and removes it from lists and charts. The reason is stored as FALSE_POSITIVE: <reason>. This cannot be undone through the API.

Terminal window
curl -s -X PATCH \
-H "Authorization: Bearer $THORIUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reason": "Lag spike, player was on a boat"}' \
"https://api.thorium.ac/api/v1/players/alerts/9a1d0a0e-2f3b-5c4d-8e6f-0123456789ab/dismiss?orgId=$ORG_ID"

Returns 204 No Content.

Review many at once

PATCH /players/alerts/bulk · scope ManageDetections

Acknowledges or dismisses up to 100 detections with one reason. Ids that are not on this network, or are already dismissed, are skipped.

Terminal window
curl -s -X PATCH \
-H "Authorization: Bearer $THORIUM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"ids": ["9a1d0a0e-2f3b-5c4d-8e6f-0123456789ab", "4e8f2a6c-1b3d-4f5a-9c7e-0d2b4f6a8c1e"],
"action": "acknowledge",
"reason": "Weekly review"
}' \
"https://api.thorium.ac/api/v1/players/alerts/bulk?orgId=$ORG_ID"
{ "updated": 2 }

action is acknowledge or dismiss. 400 for a missing reason, another action, or an empty or oversized ids list.

Replays

Many Minecraft detections come with a replay: the few seconds of movement, combat and world data around the detection. A replay’s id is the detection’s id. The replay endpoints are not rate limited.

Check a replay exists

HEAD /replay/exists?replayId={id}&orgId={orgId} · scope ViewAlerts

200 if there is a replay, 404 if not.

Terminal window
curl -s -o /dev/null -w "%{http_code}\n" -I \
-H "Authorization: Bearer $THORIUM_API_KEY" \
"https://api.thorium.ac/api/v1/replay/exists?replayId=9a1d0a0e-2f3b-5c4d-8e6f-0123456789ab&orgId=$ORG_ID"

Replay metadata

GET /replay/alert?replayId={id}&orgId={orgId} · scope ViewAlerts

{
"id": "9a1d0a0e-2f3b-5c4d-8e6f-0123456789ab",
"alertType": "MCSpeed",
"acknowledged": false,
"createdAt": "2026-09-28T17:50:02.114Z",
"details": "{\"reason\":\"speed\",\"obs\":0.412,\"lim\":0.351,\"mods\":[\"eff:speed:1\"],\"ticks\":6,\"ping\":85}",
"serverId": "11111111-2222-4333-8444-555555555555",
"serverName": "Survival",
"playerUuid": "069a79f4-44e9-4726-a5be-fca90e38aaf5",
"playerUsername": "Notch"
}

Download a replay

GET /replay/download?replayId={id}&orgId={orgId} · scope ViewAlerts

Returns the replay as application/octet-stream, named {id}.bin.

Terminal window
curl -s -H "Authorization: Bearer $THORIUM_API_KEY" -o 9a1d0a0e.bin \
"https://api.thorium.ac/api/v1/replay/download?replayId=9a1d0a0e-2f3b-5c4d-8e6f-0123456789ab&orgId=$ORG_ID"

The file is Thorium’s own capture format, for archiving or attaching to a ticket. To watch it, open https://thorium.ac/dashboard/replay/{id} in the dashboard. Replays follow your plan’s history window.