Documentation
One HTTP API per server, in JSON (or plain text for clients without a parser — including the Paper plugin itself). It is used to request reports and 3D renders, follow their progress and manage shared report links. It never exposes how a score is computed — see how it works for that.
One base URL per server (https://<your-id>.tunnelvision.fr), one Bearer token per server. GET /report for a list of sessions to check, POST /render for the interactive 3D scene. Both switch to an asynchronous job beyond a certain size — you get an id to poll rather than a request that hangs.
The API responses quoted below are shown exactly as the service returns them: some values and error messages are in French for now (verdict, detail). Field names and status codes are stable.
Every server gets a unique API token (shown once, at creation). All routes require it, except GET /health (used by the Docker healthcheck) and reading a shared report link (GET /hosted/<token>, protected by the share token itself rather than the API token).
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-id.tunnelvision.fr/health
A compromised token only opens your server's data — each server lives in its own isolated space (see the privacy page). You can regenerate it at any time; the old one stops working immediately.
| Method | Path | Purpose |
|---|---|---|
| GET | /health | Server state: mirror present, available data window, running jobs. No authentication. |
| POST | /sync | Synchronises the local mirror from your Minecraft server, without running an analysis. |
| GET | /report | Sessions sorted by suspicion score, over a given window. |
| GET | /report/jobs/{id} | Progress / result of a report that went to an asynchronous job. |
| POST | /render | Starts an interactive 3D rebuild over a window (always asynchronous). |
| GET | /render/{id} | Progress / files produced by a render. |
| GET | /renders | This server's render history. |
| GET | /reports/{file} | Downloads a render page produced by /render. |
| GET | /hosted-reports | Reports currently shared by link (still valid). |
| POST | /hosted-reports/{id}/revoke | Cuts a shared report link immediately. |
| POST | /hosted-reports/{id}/relink | Revokes the old link and generates a new one. |
| GET | /jobs | Every job (reports + renders) for this server. |
| GET | /audit | Audit log: what came in, what was produced, when, by whom. |
| GET | /live | Real-time stream (Server-Sent Events) of the same content as /report. |
GET /report takes a window (start/end, ISO UTC), a watched ore (ore, e.g. diamond), and a few filtering settings (gap_seconds, min_blocks, min_ore_blocks, ore_families) — the defaults are right in the vast majority of cases.
curl -H "Authorization: Bearer YOUR_TOKEN" \ "https://your-id.tunnelvision.fr/report?ore=diamond&start=2026-07-01&end=2026-07-08"
Response (abridged):
{
"target": "diamond",
"sessions": [
{
"pseudo": "Steve",
"world": "world",
"session_id": "...",
"score": 82,
"verdict": "fortement suspect"
}
]
}
A modest window answers directly. A wider one (a lot of blocks to process) answers 202Sent to an asynchronous jobThe window is too expensive to answer straight away — a job id is returned, to be polled until it completes.{"job_id": "a1b2c3d4e5f6", "status": "queued", "poll": "/report/jobs/a1b2c3d4e5f6"} with a job id to poll on GET /report/jobs/{id} until status=done — see the next section. Add ?format=text for key=value lines instead of JSON (that is what the plugin reads, since it ships no JSON parser).
POST /render works on the same principle for the interactive 3D scene, always as a job (a render takes several seconds at the very least):
curl -X POST -H "Authorization: Bearer YOUR_TOKEN" \ "https://your-id.tunnelvision.fr/render?start=2026-07-01&end=2026-07-08&host=true" → {"job_id": "a1b2c3d4e5f6", "status": "queued", "poll": "/render/a1b2c3d4e5f6"} curl -H "Authorization: Bearer YOUR_TOKEN" \ https://your-id.tunnelvision.fr/render/a1b2c3d4e5f6
error)
A job interrupted by a restart (update, incident) turns into an explicit error rather than vanishing — its state survives on disk. With host=true, and if your server is eligible for online hosting, the response also contains hosted_url: an unguessable, time-limited link that anyone who receives it can open without an API token (handy for sharing a case with other moderators). Manage those links through /hosted-reports (list, revoke, regenerate).
Before running any computation, the API estimates the memory cost of the requested window (how many blocks are involved) and refuses upfront (422Window refused before any computationThe RAM predicted for this window exceeds the server's budget.{"detail": "Fenetre trop couteuse : ~2.4M blocs, ~2.1 Go predits (budget 1.6 Go)."}) if it exceeds the server's budget — rather than risking a crash halfway through. The error response always includes a suggested window that would pass:
{
"detail": "Fenetre trop couteuse : ~2 400 000 blocs, ~2.1 Go de RAM predits (budget 1.6 Go). Reessayez avec une fenetre d'environ 12 jour(s)."
}
Other bounds worth knowing: 31 days maximum per render (/render), a single heavy report and a single render at a time per server (a second simultaneous request gets 409Already runningA report or render of the same kind is already running on this server — try again once it finishes.{"detail": "Un rapport est deja en cours pour ce serveur."}), and a window cannot reach back beyond your server's retention period (90 rolling days by default — 422Outside the retention periodThe requested window reaches back before the start of the data available on this server.{"detail": "Fenetre hors retention : donnees disponibles depuis 2026-05-20."} with the cut-off date if you go past it).
GET /health exposes mirror_since / mirror_until: the data window actually available on your server right now, worth checking before building a request.
| Code | Meaning |
|---|---|
| 400 | Invalid parameter (unknown ore, render window too wide, malformed filter). |
| 401 | Missing or invalid token. |
| 404 | Unknown job or hosted report. |
| 409 | A report or render of the same kind is already running on this server — try again once it finishes. |
| 410 | Shared report link expired, revoked, or its file has been purged. |
| 422 | Window refused by admission control or outside the retention period (see above). |
| 502 | Synchronisation with your Minecraft server failed (gateway unreachable). |
During the beta, access is requested through a short form and handled by hand. Request access — or reach us directly on Discord / by email.