Skip to content

API Reference

The client API is available under /api on container port 80. JSON property names and enum values use camel case.

Method Path Success Description
GET /api/health 200 Check whether the client service is ready.
GET /api/game/status 200 Read the game state and latest operation.
GET /api/game/diagnostics 200 List retained diagnostic sessions.
GET /api/game/diagnostics/{sessionId} 200 Download a session diagnostic ZIP.
GET /api/game/players 200 Read the live local player and client-tracked players.
PUT /api/game/options 204 Replace Minecraft options.txt.
POST /api/game/start/vanilla 202 Start a Mojang vanilla release.
POST /api/game/start/neoforge 202 Start a NeoForge release, latest stable by default.
POST /api/game/start/curseforge 202 Start a CurseForge modpack file.
POST /api/game/connect 200 Converge the running game on a server connection.
POST /api/game/send-chat 204 Send chat text or a command.
GET /api/game/screenshot 200 Return the Minecraft window as image/png.
POST /api/game/stop 200 Stop the current game.

GET /api/game/status returns the current state and the most recently accepted operation:

{
"state": "ready",
"operationId": 1,
"sessionId": "16721635-5993-47fc-827e-727262544c24",
"operation": "start-vanilla",
"operationState": "succeeded",
"processId": 432,
"exitCode": null,
"server": null,
"message": "Game window is ready",
"error": null,
"failure": null,
"warnings": [],
"updatedAt": "2026-08-12T12:00:00+00:00"
}
Field Description
state Game lifecycle: idle, starting, ready, connected, stopping, or failed.
operationId Increasing identifier for the latest accepted operation.
sessionId Diagnostic session identifier assigned to an accepted launch; null before the first launch. Retained after stop until another launch is accepted.
operation Latest operation: start-vanilla, start-neoforge, start-curseforge, options, connect, send-chat, screenshot, or stop; otherwise null.
operationState none, running, succeeded, failed, or canceled.
processId Minecraft process identifier while it is running.
exitCode Minecraft exit code after it exits, when available.
server Connected host and port, otherwise null.
message Short description of the current result.
error Failure message, otherwise null.
failure Structured runtime failure with code, operation, stage, message, exceptionType, and full stackTrace; otherwise null.
warnings Non-fatal messages associated with the state.
updatedAt ISO 8601 timestamp of the latest status change.

Start operations return this status with 202 Accepted and a Location: /api/game/status header. Save its operationId, then poll until the same operation reports ready and succeeded. Stop polling with a failure if the identifier changes or the operation becomes failed or canceled.

Runtime failures are also returned in the failure extension of HTTP Problem Details responses. An unexpected Minecraft process exit uses client.process.exited; a cgroup-confirmed OOM kill uses client.process.out_of_memory and reports the exit code and configured heap. Stack traces intentionally expose container implementation details and should not be forwarded to untrusted consumers.

GET /api/game/players reads players from the running Minecraft client. It does not require a mod, server plugin, or protocol-specific configuration.

{
"local": {
"uuid": "f84c6a79-7f8b-4b7c-a02a-7bc7e3f99574",
"name": "LocalPlayer",
"position": { "x": 0.5, "y": 64.0, "z": 0.5 },
"body": { "yaw": 15.0 },
"head": { "yaw": 20.0, "pitch": -5.0 }
},
"remote": [
{
"uuid": "62ca1f73-b4d1-47bb-b176-052905a08b35",
"name": "Neighbour",
"position": { "x": 2.5, "y": 64.0, "z": 0.5 },
"body": { "yaw": -10.0 },
"head": { "yaw": -5.0, "pitch": 2.5 }
}
]
}

Every returned player always contains finite x, y, and z coordinates plus finite body yaw, head yaw, and head pitch values in degrees. Profile identity is null when unavailable. A client without a current player world returns 409 Conflict; tracker initialization or complete position or rotation failures return 503 Service Unavailable rather than partial player data.

Terminal window
curl --fail http://localhost:8080/api/health

A ready API returns 200 OK with ok as plain text. An unavailable client service returns 503 Service Unavailable.

Send the complete options.txt contents as text/plain:

Terminal window
curl --fail-with-body \
--request PUT \
--header 'Content-Type: text/plain' \
--data-binary $'fullscreen:false\nrenderDistance:2\nenableVsync:false' \
http://localhost:8080/api/game/options

The options are retained in the configured Minecraft directory and used by later launches.

Only one game can run at a time. Every start request accepts an optional arguments array containing PortableMC start arguments and an optional positive memoryMb integer. When provided, memoryMb sets the Minecraft JVM maximum heap through -Xmx; it does not limit total container memory. Do not also provide a raw -Xmx argument.

Mojang used a 2 GiB default before 26.1 and changed it to 4 GiB for 26.1. See Minecraft Java Edition memory allocation and the Minecraft Java Edition 26.1 release notes.

version is a Mojang release identifier:

Terminal window
curl --fail-with-body \
--request POST \
--header 'Content-Type: application/json' \
--data '{"version":"1.21.8","memoryMb":2048,"arguments":["--username","TestPlayer"]}' \
http://localhost:8080/api/game/start/vanilla

version is an optional Minecraft release identifier, the same form the vanilla endpoint accepts. Omit it, or send a blank value, to resolve and start the latest stable NeoForge release:

Terminal window
curl --fail-with-body \
--request POST \
--header 'Content-Type: application/json' \
--data '{"memoryMb":4096,"arguments":["--username","TestPlayer"]}' \
http://localhost:8080/api/game/start/neoforge

Supply it to start NeoForge for a specific Minecraft version instead:

Terminal window
curl --fail-with-body \
--request POST \
--header 'Content-Type: application/json' \
--data '{"version":"1.21.1","memoryMb":2048,"arguments":["--username","TestPlayer"]}' \
http://localhost:8080/api/game/start/neoforge

Sodium is installed automatically when Modrinth provides a compatible NeoForge build for the resolved Minecraft version.

Provide a project slug and positive CurseForge fileId. The container must have CURSEFORGE_API_KEY configured.

Terminal window
curl --fail-with-body \
--request POST \
--header 'Content-Type: application/json' \
--data '{"slug":"all-the-mods-10","fileId":1234567,"memoryMb":4096,"arguments":["--username","TestPlayer"]}' \
http://localhost:8080/api/game/start/curseforge

The game must be running. port must be between 1 and 65535. Connection navigation is idempotent for a given host and port: repeated callers join the same background operation, and an already-connected game returns its original result. A different target is rejected with 409 Conflict while connecting or connected.

Canceling an HTTP caller stops only that caller’s wait. The background navigation continues from whichever Minecraft screen is currently visible until the game connects, exits, or is stopped through the API.

Terminal window
curl --fail-with-body \
--request POST \
--header 'Content-Type: application/json' \
--data '{"host":"minecraft-server","port":25565}' \
http://localhost:8080/api/game/connect

The confirmed connection returns:

{
"server": {
"host": "minecraft-server",
"port": 25565
},
"connectedAt": "2026-08-12T12:01:00+00:00"
}

The game must be connected. A leading / sends a Minecraft command.

Terminal window
curl --fail-with-body \
--request POST \
--header 'Content-Type: application/json' \
--data '{"message":"Hello from the client"}' \
http://localhost:8080/api/game/send-chat

The game must be ready or connected.

Terminal window
curl --fail \
--output minecraft.png \
http://localhost:8080/api/game/screenshot
Terminal window
curl --fail-with-body \
--request POST \
http://localhost:8080/api/game/stop

The response contains mode and the final status:

{
"mode": "graceful",
"status": {
"state": "idle",
"operationId": 5,
"operation": "stop",
"operationState": "succeeded",
"processId": null,
"exitCode": 0,
"server": null,
"message": "Game stopped",
"error": null,
"warnings": [],
"updatedAt": "2026-08-12T12:02:00+00:00"
}
}

mode is alreadyStopped, graceful, or forced.

Errors use the standard Problem Details JSON format.

Status Meaning
400 Bad Request A required value is missing or invalid.
408 Request Timeout The request was canceled.
409 Conflict The requested action is not valid for the current game state or another operation is running.
500 Internal Server Error The operation failed unexpectedly.
503 Service Unavailable The client service is not ready.
504 Gateway Timeout A client operation exceeded its timeout.

Each accepted launch creates a diagnostic session before preparation begins. A session includes early launch failures as well as evidence from a running game. Stopping Minecraft does not erase it, and subsequent launches receive different identifiers.

GET /api/game/diagnostics returns sessions newest first:

[
{
"sessionId": "16721635-5993-47fc-827e-727262544c24",
"launch": "start-vanilla:1.21.1:",
"startedAt": "2026-09-05T12:00:00+00:00",
"endedAt": "2026-09-05T12:02:00+00:00",
"status": null,
"lastFailure": null,
"warnings": [],
"downloadUrl": "/api/game/diagnostics/16721635-5993-47fc-827e-727262544c24"
}
]

status contains the latest game status recorded for that session when available. lastFailure preserves the most recent structured failure even when a successful stop has cleared the current status error. warnings describes missing, truncated, or unavailable evidence. endedAt is null until the session is finalized.

GET /api/game/diagnostics/{sessionId} returns 200 OK with Content-Type: application/zip and a download filename. An unknown or expired session returns 404 Not Found.

Save the sessionId from the accepted start response and use that exact identifier during cleanup:

Terminal window
curl --fail --silent --show-error \
--request POST http://localhost:8080/api/game/start/vanilla \
--header 'Content-Type: application/json' \
--data '{"version":"1.21.1","arguments":["--username","TestPlayer"]}' \
--output launch.json
client_session_id=$(jq -r '.sessionId' launch.json)
# Poll readiness, connect, and perform the test before cleanup.
curl --fail --request POST http://localhost:8080/api/game/stop
curl --fail --silent --show-error \
"http://localhost:8080/api/game/diagnostics/$client_session_id" \
--output "client-diagnostics-$client_session_id.zip"

Download before removing the container. Downloads also work while Minecraft runs; they capture evidence available at that moment without changing the current operation or game state.

The ZIP contains available evidence:

File Contents
session.json Session identity, launch description, timestamps, latest status, last failure, and collection warnings.
operations.jsonl Timestamped game status transitions and operation outcomes.
console-*.log Recent launcher, Minecraft, agent, and preparation output.
previous-*.log, previous-operations.jsonl Previous retained segment after output rotation.
logs-latest.log, logs-debug.log Recent Minecraft log contents changed during this session.
debug-disconnect-*.txt Minecraft disconnect reports from this session.
crash-reports-crash-*.txt Minecraft crash reports from this session.
failure-*.png Best-effort failure screenshot, when the matching game window remains available.

Missing reports or failed screenshots do not replace the original game error. Screenshot collection is limited to three seconds. Console output continues to appear in Docker logs. The bundle excludes credential stores and agent authentication tokens, but game logs and screenshots can still contain player names, server addresses, and chat.

Configure the container with Docker environment variables:

Variable Default Meaning
VOID_DIAGNOSTICS_DIRECTORY /var/lib/void-client/diagnostics Session evidence directory.
VOID_DIAGNOSTICS_MAXIMUM_SESSIONS 10 Maximum retained sessions; oldest completed sessions expire first.
VOID_DIAGNOSTICS_MAXIMUM_TOTAL_MB 256 Total storage budget in MiB.
VOID_DIAGNOSTICS_MAXIMUM_SESSION_MB 32 Storage budget per session in MiB.

Limits must be positive and the total budget must accommodate one session. Individual evidence files are capped at 2 MiB; console and operation logs rotate to retain recent output. Collection warnings identify truncation and omitted evidence. Active sessions are protected from eviction, so reaching a storage limit can omit additional evidence until space becomes available.

Storage survives game stop and start within the same container. For persistence after container removal, mount a volume at the diagnostics directory, for example --volume client-diagnostics:/var/lib/void-client/diagnostics. Stored sessions are discovered when the API starts again.