API reference

Every curated route below wraps Moonraker with clean JSON in and out. Anything not listed here is still reachable through the raw passthrough, and anything Klipper pushes live is available over the websocket.

All paths are relative to this server, e.g. http://192.168.1.50:8080/api/temps. Every response is JSON. Errors come back as { "error": "message" } with a matching HTTP status - Moonraker's own error status/body is forwarded through unchanged wherever possible.

Authentication & API Keys

EnderCTL supports both interactive 2FA session tokens and persistent API keys for external scripts and automations.

headerx-api-key: <key>

Attach your persistent API key to any HTTP request. API keys can be generated in the WebUI (System → API Keys & Authentication) or set globally via WRAPPER_API_KEY in .env.

curl -H "x-api-key: ek_live_..." http://localhost:8080/api/temps
headerAuthorization: Bearer <token_or_key>

Standard HTTP Bearer authorization. Accepts both persistent API keys and active 30-day 2FA session tokens generated upon mobile approval.

curl -H "Authorization: Bearer ek_live_..." http://localhost:8080/api/temps
GET/api/auth/status

Returns whether 2FA is active and whether the current caller's credentials are valid.

Response

{ "authEnabled": true, "authenticated": true, "hasMasterApiKey": false, "customKeysCount": 2 }
GET/api/auth/keys

Lists configured API keys (masked prefixes, labels, creation dates).

Response

{ "keys": [ { "id": "7a9c1e2b", "label": "Home Assistant Integration", "prefix": "ek_live_9f...4a12", "createdAt": 1789240000000 } ] }
POST/api/auth/keys

Generates a new persistent API key with a custom label.

Body

{ "label": "Home Assistant" }

Response

{ "ok": true, "key": { "id": "7a9c1e2b", "label": "Home Assistant", "key": "ek_live_8f3910c28340192830192830", "createdAt": 1789240000000 } }
DELETE/api/auth/keys/:id

Revokes an existing API key by ID.

Response

{ "ok": true }

Health

GET/api/health

Confirms the wrapper is running and shows which Moonraker host it's pointed at.

Response

{ "ok": true, "moonraker": "http://192.168.1.224:7125" }

Temperature

Extruder and bed heater readouts and targets.

GET/api/temps

Current temperature, target, and heater power for the nozzle and bed.

Response

{ "extruder": { "temperature": 204.8, "target": 205, "power": 0.31 }, "heater_bed": { "temperature": 59.9, "target": 60, "power": 0.18 } }
POST/api/temps/extruder

Sets the nozzle target temperature (M104).

Body

ParamTypeRequiredNotes
tempnumberyesTarget °C. Use 0 to turn the heater off.

Example

curl -X POST /api/temps/extruder -H "Content-Type: application/json" -d '{"temp": 210}'
POST/api/temps/bed

Sets the bed target temperature (M140).

Body

ParamTypeRequiredNotes
tempnumberyesTarget °C. Use 0 to turn the heater off.

Example

curl -X POST /api/temps/bed -H "Content-Type: application/json" -d '{"temp": 60}'
POST/api/temps/cooldown

Sets both the nozzle and bed target to 0. No body required.

Motion

Homing, jogging, extrusion, and the emergency stop.

POST/api/motion/home

Homes one or more axes (G28).

Body

ParamTypeRequiredNotes
axesstringnoe.g. "XY". Omit or send an empty string to home all axes.
POST/api/motion/move

Moves the toolhead (G1). Any axis you omit stays put.

Body

ParamTypeRequiredNotes
x, y, znumbernoTarget position in mm (or delta, if relative).
feedratenumbernomm/min. Defaults to 3000.
relativebooleannoIf true, x/y/z are deltas (G91); otherwise absolute (G90).

Example - jog +1mm on X

curl -X POST /api/motion/move -H "Content-Type: application/json" \ -d '{"x": 1, "relative": true, "feedrate": 3000}'
POST/api/motion/extrude

Extrudes or retracts filament. Negative amounts retract.

Body

ParamTypeRequiredNotes
amountnumberyesmm. Negative to retract.
feedratenumbernomm/min. Defaults to 300.

Make sure the nozzle is at temperature first - Klipper will reject cold extrusion.

GET/api/motion/position

Current toolhead position and which axes are homed.

Response

{ "toolhead": { "position": [120.0, 110.0, 5.2, 0], "homed_axes": "xyz" } }
POST/api/motion/estop

Emergency stop. Immediately kills steppers and heaters through Moonraker's /printer/emergency_stop. Requires a firmware restart to recover from. No body required.

Fans

On this printer fan0 and MainBoardFan are raw output_pin sections, not Klipper fan objects - they're driven with SET_PIN, not M106. nozzle_fan is a heater_fan, auto-controlled by extruder temp and exposed read-only.

GET/api/fans

Lists every configured fan and its current value.

Response

{ "controllable": [ { "name": "fan0", "label": "Part cooling fan", "value": 1 }, { "name": "MainBoardFan", "label": "Mainboard/PSU fan", "value": 1 } ], "autoControlled": [ { "name": "nozzle_fan", "note": "driven automatically…", "speed": 1 } ] }
POST/api/fans/:name

:name must be fan0 or MainBoardFan.

Body

ParamTypeRequiredNotes
valuenumberyes0 to 1 (0% - 100%).

Example

curl -X POST /api/fans/fan0 -H "Content-Type: application/json" -d '{"value": 0.5}'

Sensors

GET/api/sensors

Every temperature_sensor/temperature_fan plus the extruder and bed heater objects, in one call.

GET/api/sensors/objects

The raw list of every Klipper object currently available - useful for discovering anything you add to printer.cfg later.

POST/api/sensors/input-shaper/calibrate

Runs a resonance test using the ADXL345. This moves the toolhead - clear the bed first.

Body

ParamTypeRequiredNotes
axis"x" | "y" | "all"noDefaults to "all".

Macros

GET/api/macros

Every gcode_macro defined in printer.cfg and its includes.

Response

{ "macros": ["START_PRINT", "END_PRINT", "PAUSE"] }
POST/api/macros/run

Runs a named macro, optionally with parameters.

Body

ParamTypeRequiredNotes
namestringyesMacro name, e.g. "START_PRINT".
paramsobjectnoKey/value pairs, sent as KEY=value.

Example

curl -X POST /api/macros/run -H "Content-Type: application/json" \ -d '{"name": "START_PRINT", "params": {"BED_TEMP": 60, "EXTRUDER_TEMP": 210}}'
POST/api/macros/gcode

Escape hatch for raw, possibly multi-line, G-code.

Body

ParamTypeRequiredNotes
scriptstringyesOne or more G-code lines, separated by \n.

Files & print jobs

GET/api/files

Lists G-code files stored on the printer (the gcodes root).

GET/api/files/:filename/metadata

Slicer-embedded metadata for a file: thumbnail list, estimated print time, filament used, slicer name/version - whatever the slicer wrote into the gcode header.

GET/api/files/:filename/thumbnail

Proxies the slicer-embedded preview image as image/png, so the webUI can show a model preview without the browser needing direct network access to the printer's own Moonraker port. Returns 404 if the file wasn't sliced with thumbnails enabled.

Query params

ParamTypeRequiredNotes
size"small" | "large"noPicks the smallest or largest embedded thumbnail. Defaults to largest.

Example

<img src="/api/files/part.gcode/thumbnail?size=small">
GET/api/files/:filename/toolpath

Parses the file's own gcode into a compact set of extrusion-move line segments for the dashboard's live 3D preview: one line per extrusion move, each tagged with the byte offset it occurs at (to match against virtual_sdcard.file_position) and which layer it belongs to. This is the actual toolpath, not the original 3D model - there's no STL/3MF on the printer to reconstruct a solid mesh from.

Response

{ "totalLines": 48213, "fileByteLength": 1842004, "segmentCount": 31500, "downsampled": false, "layerCount": 212, "bounds": { "minX": 32.1, "maxX": 187.4, … }, "segments": { "positions": [x0,y0,z0, x1,y1,z1, …], "byteOffsets": […one per segment…], "layerIndex": […one per segment…] } }

Limits & approximations

WhatBehavior
File sizeFiles over 80 MB return 413 - parsing is done in memory, and this keeps it fast and safe on small SBCs.
Very large toolpathsSegments are downsampled (evenly, keeping order) to a 200,000 cap; downsampled: true flags this.
LayersInferred from Z increasing during an extrusion move - a heuristic, not a true per-object layer count.
Arcs (G2/G3)Approximated as a straight chord to the endpoint.
Byte offsetsAssume \n line endings; CRLF files undercount by ~1 byte/line - fine for a progress heatmap, not exact.
POST/api/files/upload

multipart/form-data upload of a single file.

Body

FieldTypeRequiredNotes
filefileyesUp to 500 MB.

Example

curl -X POST /api/files/upload -F "[email protected]"
DELETE/api/files/:filename

Removes a file from the printer's gcodes folder.

POST/api/files/:filename/print

Starts printing a file already on the printer.

POST/api/print/pause

Pauses the active print. No body required.

POST/api/print/resume

Resumes a paused print. No body required.

POST/api/print/cancel

Cancels the active print. No body required.

System, power & lights

GET/api/system/status

The "everything at once" snapshot: printer info, temps, position, and update-manager status in one call.

GET/api/system/info

Moonraker + host machine system info (CPU, distro, network interfaces, etc).

POST/api/system/restart-klipper

Restarts the Klipper service. Interrupts any active print. No body required.

POST/api/system/firmware-restart

Restarts the MCU firmware connection. Interrupts any active print. No body required.

GET/api/system/power/devices

Lists power devices configured in moonraker.conf (empty until you add one).

POST/api/system/power/:device/:action

:action is on or off. No body required.

GET/api/system/lights

Returns { lights: [] } today - no neopixel/output_pin light exists in printer.cfg on this printer. Wire an LED strip to a spare GPIO, declare it in printer.cfg, and add its name to hardware.lights in src/config.js to light this endpoint up.

Raw passthrough

ALL/api/raw/*

Mirrors the method, path, query params, and JSON body straight onto Moonraker's own REST API. This guarantees nothing Moonraker exposes is ever blocked here, even endpoints added by a future Moonraker or Creality-Helper-Script update that the curated routes above don't know about yet.

Example

curl /api/raw/printer/objects/list curl -X POST /api/raw/printer/print/start -H "Content-Type: application/json" -d '{"filename": "part.gcode"}'

Websocket

WSws://<host>:8080/ws

One persistent connection, fanned out to every client. Reconnects automatically to Moonraker if the printer drops off Wi-Fi or reboots.

Messages you'll receive

{ "type": "status", "data": { "extruder": {…}, "heater_bed": {…}, "toolhead": {…}, "print_stats": {…} } } { "type": "connection", "data": "moonraker_connected" } { "type": "gcode_response", "data": "ok" }

Any other Moonraker notification (klippy state changes, file-list changes, job-queue events, power-device changes) is forwarded too, as { type: <method>, data: <params> }, so nothing is silently dropped.

What you can send

{ "action": "gcode", "script": "G28" }