REST API Reference
The bambu-mcp daemon serves an HTTP API alongside MCP: 87 routes, 91 route and method pairs. Every route acts on a printer through the same live session the MCP tools use.
Base URL: http://<host>:<api_port>/api. The port is not fixed. It comes from a shared pool that starts at 49152. Discover it with the get_server_info MCP tool, or by probing GET /api/server_info from port 49152 upward.
Authentication: none. The API listens on all network interfaces. Anyone who can reach the port can read printer state and send commands. Keep it on a trusted network or behind a firewall.
Printer selection: most routes take a printer name. A ⚠️ at the start of a summary marks a write operation.
OpenAPI: the running server publishes GET /api/openapi.json and a Swagger UI at GET /api/docs. This page is generated from that same document.
Contents
- System (33)
- Climate (8)
- Print Control (9)
- AMS & Filament (10)
- Hardware (10)
- Files (17)
- Camera (1)
System
Health, session management, logging, and server information.
DELETE, GET /api/alerts
Return or clear pending state-change alerts for a printer.
Query parameters:
printer — printer name (required)
all — "true" to return alerts for all printers (GET only; ignores printer param)
GET returns pending alerts (clears queue by default). DELETE clears the queue without returning alerts.
Parameters
printer(string, query, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.all(boolean, query)
Responses
200: Success
GET /api/charts
Render and return the live telemetry dashboard HTML for a printer.
GET /api/charts?printer=<name> Returns Content-Type: text/html. SVG panels refresh via AJAX every 30 s.
Parameters
printer(string, query)
Responses
200: Success
GET /api/charts_panels
Return refreshed SVG panels as JSON for AJAX updates.
GET /api/charts_panels?printer=<name> Returns {"panels": [svg, ...], "ts": "<timestamp>"} or {"error": "..."}.
Parameters
printer(string, query)
Responses
200: Success
GET /api/default_printer
Return the printer that would be targeted by a request with no explicit printer parameter.
Resolves the default printer using the same three-tier logic as all printer-specific routes:
1. printer query parameter (explicit — always wins)
2. BAMBU_API_PRINTER environment variable
3. First entry in the connected printers list (non-deterministic — use explicit targeting)
Use this route to discover which printer to pass as the printer parameter on write routes.
The printer parameter is required on all printer-specific routes; this route provides the
discovery mechanism.
Response fields:
printer: resolved printer name, or null if no printers are connectedsource: resolution path — "explicit", "env_var", "first_connected", or "none"connected_printers: all currently connected printer names
Responses
-
200: Success
GET /api/docs
Serve Swagger UI for interactive API exploration.
Responses
-
200: Success
GET /api/docs/
Serve Swagger UI for interactive API exploration.
Responses
-
200: Success
GET /api/dump_log
Return the bambu-mcp server log.
Responses
-
200: Success
GET /api/filament_catalog
Return the full filament profile catalog as a JSON array.
Each entry contains: tray_info_idx, name, vendor, filament_type, nozzle_temp_min, nozzle_temp_max, hot_plate_temp.
No printer parameter required — this is static reference data from BPM.
Responses
200: Success
GET /api/find_3mf_by_id
Search SD card 3MF file tree by full path ID. ?id=<path>
Parameters
printer(string, query, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.id(string, query)
Responses
200: Success
GET /api/find_3mf_by_name
Search SD card 3MF file tree by filename. ?name=<filename>
Parameters
printer(string, query, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.name(string, query)
Responses
200: Success
GET /api/health_check
Return health status and full printer state.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
GET /api/log_level
Return the current root log level name.
Responses
200: Success
GET /api/monitoring_data
Return all telemetry history fields as raw JSON (rolling 60 min).
Parameters
printer(string, query, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
200: Success
GET /api/monitoring_history
Return telemetry history summary (default) or full raw time-series (raw=true).
Parameters
printer(string, query, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.raw(boolean, query)
Responses
200: Success
GET /api/monitoring_series
Return the rolling 60-min time-series for a single telemetry field.
Parameters
printer(string, query, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.field(string, query, required)
Responses
200: Success
GET /api/openapi.json
Return the OpenAPI 3.0 specification for this API.
Add ?pretty=true for indented JSON (~168 KB). Useful for inspecting large payloads and for testing gzip compression against over-threshold text responses.
Parameters
pretty(string, query)
Responses
-
200: Success
GET /api/printer
Return full printer state as JSON.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
GET /api/printers
Return all configured printers and their current connection status.
No printer parameter required — this is server-level discovery data.
Response fields:
printers: list of dicts, each withname,connected,session_activetotal: count of configured printers
Use this route to discover which printer names are available before calling
printer-specific routes. Equivalent to the MCP get_configured_printers() tool.
Responses
-
200: Success
PATCH /api/rename_printer
Rename the printer on its own firmware. ?new_name=<name>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.new_name(string, exampleMy Printer): New display name to set on the printer firmware (visible on touchscreen and in Bambu Studio).
Responses
-
200: Success
GET /api/server_info
Return runtime port pool state for the bambu-mcp server.
No printer parameter required — this is server-level state.
Returns:
api_port — TCP port the REST API is currently bound to
api_url — convenience base URL: http://localhost:{api_port}/api
pool_start — first port in the shared ephemeral pool (default 49152)
pool_end — last port in the shared ephemeral pool inclusive (default 49251)
pool_size — total number of ports in the pool
pool_available — number of unclaimed ports remaining
pool_claimed — sorted list of all currently claimed port numbers (API + MJPEG streams)
stream_count — number of active MJPEG camera streams
streams — {printer_name: {port, url}} for each active stream
Responses
-
200: Success
GET /api/session_status
Return the MQTT session state and connectivity for a printer.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
POST /api/set_bpm_verbose
Toggle raw MQTT payload logging for a live printer session, no restart.
Flips BambuConfig.verbose directly on the running session's config object (a plain mutable field bpm reads fresh on every _on_message call) — distinct from the bpm logger's level, which set_log_level's bpm_level already covers live. Both loggers and every session boot at ERROR/verbose=False; this and set_log_level are the only controls — no env var seeds either. Resets to that boot default on the next daemon restart (in-memory only).
Query params: printer=<name> (optional, falls back to default) verbose=true|false
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.verbose(string)
Responses
200: Success
POST /api/set_log_level
Change the runtime log level.
Query params: level=DEBUG|INFO|WARNING|ERROR|CRITICAL bpm_level=DEBUG|INFO|WARNING|ERROR|CRITICAL (optional, set the bpm logger independently of root)
Request body (application/x-www-form-urlencoded or application/json)
level(string)bpm_level(string)
Responses
200: Success
GET /api/snapshot
Return a single still frame from the printer camera.
Query parameters:
printer — printer name (required)
resolution — "native" | "1080p" | "720p" | "480p" | "360p" | "180p" (default "native")
quality — JPEG quality integer 1–100 (default 85), written as plain ASCII digits (at most 6:
"85" or "085"); any other value (out of range, or not plain digits: "5_0", "+85",
" 85", "٨٥", "85.5") is answered 400 {"error": "invalid_quality", "detail": ...}
and no frame is captured
include_status — "true" to include live print telemetry in the response
Named profiles (agent guidance):
native resolution=native quality=85 ~1–4 MB/frame Max fidelity
high resolution=1080p quality=85 ~500KB–2MB Anomaly detection
standard resolution=720p quality=75 ~200–400KB Routine analysis ⚠️ default for agents
low resolution=480p quality=65 ~80–150KB Quick status checks
preview resolution=180p quality=55 ~20–40KB Thumbnails
⚠️ native resolution in polling loops burns significant tokens (4 MB/call).
Parameters
printer(string, query, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.resolution(string, query)quality(integer, query)include_status(boolean, query)
Responses
200: Success
POST /api/start_stream
⚠️ Start the local MJPEG camera stream server for a printer.
The server always runs at native resolution — per-client quality is applied by the browser tab via URL params (use /api/view_stream for parameterized tabs).
Returns:
url — base stream URL http://localhost:{port}/
port — allocated port number
protocol — "rtsps" or "tcp_tls"
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
200: Success
POST /api/stop_stream
⚠️ Stop the local MJPEG camera stream server for a printer.
Request body (JSON): printer — printer name (required)
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
200: Success
GET /api/stream_url
Return camera stream URL information for a printer.
Query parameters: printer — printer name (required)
Returns:
protocol — "rtsps", "tcp_tls", or "none"
rtsps_url — RTSPS URL (password redacted), or null
local_mjpeg_url — base URL of running MJPEG server, or null
streaming — bool: whether a stream server is currently active
Parameters
printer(string, query, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
200: Success
PATCH /api/toggle_session
Pause or resume the MQTT session for the printer.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
POST /api/trigger_printer_refresh
⚠️ Force printer to re-broadcast its full state.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
DELETE /api/truncate_log
⚠️ Truncate the bambu-mcp server log.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Responses
-
200: Success
GET /api/user_prefs
Return the stored preference for a printer+key pair.
Query parameters:
printer — printer name (required)
key — preference key, e.g. "bed_leveling" (required)
Returns {"key": "<printer>:<key>", "value": <value>} or {"value": null} if not set. No write operation — no user_permission required.
Parameters
printer(string, query, required): Printer name the preference belongs to.key(string, query, required): Preference key, e.g. bed_leveling or ams0:target_temp.
Responses
200: Success
POST /api/user_prefs
Set a stored preference for a printer+key pair.
JSON body:
printer — printer name (required)
key — preference key, e.g. "bed_leveling" (required)
value — value to store (required; any JSON-serializable type)
Returns {"success": true}. No write operation on the printer — no user_permission required.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required): Printer name the preference belongs to.key(string, required): Preference key, e.g. bed_leveling or ams0:target_temp.value(string, required): Value to store. Send a JSON body to keep its type; a form or query value is stored as a string.
Responses
200: Success
POST /api/view_stream
⚠️ Start the MJPEG stream server (if not already running) and open a browser tab.
The tab opens at the requested resolution/quality via URL params. Multiple calls with different settings open independent tabs on the same server port.
Request body (JSON):
printer — printer name (required)
resolution — "native" | "1080p" | "720p" | "480p" | "360p" | "180p" (default "native")
quality — JPEG quality integer 1–100 (default 85)
Named profiles (agent guidance):
native resolution=native quality=85 Max fidelity (default for streams)
high resolution=1080p quality=85 High detail
standard resolution=720p quality=75 Good balance
low resolution=480p quality=65 Low bandwidth
preview resolution=180p quality=55 Minimal bandwidth
Returns:
url — the parameterized URL opened in the browser
port — stream server port
protocol — "rtsps" or "tcp_tls"
opened — bool: True if browser tab was opened
overlay_active — always True
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
200: Success
Climate
Temperature targets, fan speeds, chamber light, and active tool selection.
PATCH /api/set_aux_fan_speed_target
Set aux (recirculation) fan speed. ?percent=<0-100>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.percent(integer, example70): Aux fan speed 0–100%.
Responses
-
200: Success
PATCH /api/set_bed_target_temp
Set heated bed temperature. ?temp=<°C>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.temp(integer, example35): Target bed temperature in °C. Use 0 to turn off.
Responses
-
200: Success
PATCH /api/set_chamber_target_temp
Set chamber temperature target. On H2D, sends MQTT; on A1/P1S, stores target for external chamber management. ?temp=<°C>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.temp(integer, example45): Target chamber temperature in °C. Use 0 to turn off.
Responses
-
200: Success
PATCH /api/set_exhaust_fan_speed_target
Set exhaust fan speed. ?percent=<0-100>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.percent(integer, example50): Exhaust fan speed 0–100%.
Responses
-
200: Success
PATCH /api/set_fan_speed_target
Set part-cooling fan speed. ?percent=<0-100>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.percent(integer, example100): Part cooling fan speed 0–100%.
Responses
-
200: Success
PATCH /api/set_light_state
Set chamber light. ?state=on|off
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.state(one of"on","off", required, exampleon): Light state.
Responses
-
200: Success
PATCH /api/set_tool_target_temp
Set nozzle temperature for a specific extruder (or the active tool).
Body fields:
printer — printer name (required)
temp — target temperature in °C; use 0 to turn off
extruder — (optional) 0=right nozzle, 1=left nozzle; defaults to active tool if absent
Camera scripts must use this route (Tier 1) instead of send_gcode/M104 — a dedicated route exists for this operation; raw gcode is a Tier 2 escalation violation.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.temp(integer, example220): Target nozzle temperature in °C. Use 0 to turn off.
Responses
-
200: Success
PATCH /api/toggle_active_tool
Swap active extruder between 0 (right) and 1 (left).
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
Print Control
Start, pause, resume, stop, speed, skip objects, and raw G-code.
POST /api/clear_print_error
⚠️ Clear an active print_error on the printer. ?print_error=<int>&subtask_id=<str>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Sends two commands matching the protocol BambuStudio uses when dismissing an error dialog: clean_print_error (clears the error value) and a uiop signal (acknowledges the dialog). Without both commands, the printer may re-raise the error on the next push_status.
?print_error=0 clears any active error without specifying a code.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.print_error(integer, example0): Integer error code to clear. Pass 0 to clear any active error.subtask_id(string, example ``): Subtask ID of the failed job (from get_job_info). Pass empty string if not known.
Responses
-
200: Success
POST /api/pause_printing
⚠️ Pause the current print job.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
POST /api/print_3mf
⚠️ Start printing a .3mf file from SD card. ?filename=&platenum=&plate=AUTO|COOL_PLATE|ENG_PLATE|HOT_PLATE|TEXTURED_PLATE&use_ams=true|false&ams_mapping=&bl=true|false&flow=true|false&tl=true|false
⚠️ WRITE OPERATION — requires explicit user confirmation before calling. ⛔ BLOCKED during active prints (gcode_state RUNNING or PREPARE) — returns 409. Returns 400 when bpm refuses the plate, for example use_ams=false on a dual-nozzle plate that carries no extruder map.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.filename(string, example/_jobs/myprint.gcode.3mf): Full SD card path to the .3mf file (e.g. /_jobs/myprint.gcode.3mf).platenum(integer, example1): Plate number within the .3mf project (1-based).plate(one of"auto","cool_plate","eng_plate","hot_plate","textured_plate", exampleTEXTURED_PLATE): Build plate surface type.use_ams(boolean, required, exampletrue): Print from the AMS. With no ams_mapping the mapping is resolved from the loaded spools by type and colour; the request is refused (400) when a filament has no loaded match. With use_ams=false the plate's own extruder assignment picks the external holder, and a dual-nozzle plate with no extruder map is refused (400).ams_mapping(string, required, example ``): JSON array indexed by 1-based filament id, each an absolute tray id (4-slot AMS ams_id*4+slot, AMS HT 128+slot, -1 unused), e.g. [1,-1,128]. Omit to resolve from the loaded spools — the project file carries no usable mapping.bl(boolean, required, exampletrue): Run bed leveling before printing.flow(boolean, required, examplefalse): Run flow/extrusion calibration before printing.tl(boolean, required, examplefalse): Record a timelapse of the print.
Responses
-
200: Success
POST /api/resume_printing
⚠️ Resume a paused print job.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
POST /api/send_gcode
⚠️ Send raw G-code commands. ?gcode=<commands> (use | as newline separator)
Camera script escalation tier: Tier 2 — ONLY valid when no dedicated API route covers the operation. Legitimate uses: G28 (home), G0/G1 (motion), G90/G91 (mode), M400 (wait). NOT valid for temperature: use PATCH /api/set_tool_target_temp instead (M104 via send_gcode is a Tier 1 violation when a dedicated temperature route exists).
⚠️ WRITE OPERATION — requires explicit user confirmation before calling. ⛔ BLOCKED during active prints (gcode_state RUNNING or PREPARE) — returns 409.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.gcode(string, exampleG28|G1 X100 Y100 F3000): G-code commands to send. Use | as a newline separator for multiple commands.
Responses
-
200: Success
POST /api/send_mqtt_command
⚠️ Send a raw MQTT command JSON to the printer's request topic. ?command_json=<json>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling. ⚠️ LAST-RESORT TOOL. Bypasses all safety checks. Incorrect commands can damage prints, trigger hardware faults, or put the printer into an unrecoverable state. command_json must be a valid JSON string matching the Bambu Lab MQTT command schema.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.command_json(string, example{"print":{"command":"pause"}}): Valid JSON string matching the Bambu Lab MQTT command schema.
Responses
-
200: Success
PATCH /api/set_speed_level
Set print speed level. ?level=quiet|standard|sport|ludicrous
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.level(one of"quiet","standard","sport","ludicrous", examplestandard): Print speed profile.
Responses
-
200: Success
POST /api/skip_objects
⚠️ Skip one or more objects. ?objects=<id1>,<id2>,...
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.objects(string, example1,3): Comma-separated list of object identify_id values to skip.
Responses
-
200: Success
POST /api/stop_printing
⚠️ Stop (cancel) the current print job.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
AMS & Filament
Filament loading, unloading, spool metadata, RFID refresh, and AMS settings.
POST /api/load_filament
⚠️ Load filament from an AMS slot. ?slot=<0-3>&ams_id=<int>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
ams_id is the internal unit id (AMS 2 Pro from 0, AMS HT from 128; default 0). A load
the printer's reply refuses within 5 s (for example while the unit is drying) answers
409 with the decoded reason (bpm's command_error entry in hms_errors).
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.slot(integer, example0): AMS slot index (0–3).ams_id(integer, example128): Internal AMS unit ID. AMS 2 Pro starts at 0; AMS HT starts at 128. Default 0.
Responses
-
200: Success
POST /api/refresh_spool_rfid
⚠️ Trigger RFID re-scan on an AMS slot. ?slot_id=<0-3>&ams_id=<0-n>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.slot_id(integer, example2): Slot index within the AMS unit (0–3).ams_id(integer, example0): AMS unit index (0-based).
Responses
-
200: Success
POST /api/select_extrusion_calibration
⚠️ Select an extrusion calibration profile for a filament slot. ?tray_id=<int>&cali_idx=<int>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
tray_id encoding: ams_unit_index × 4 + slot (0–3). External spool: 254 (single-nozzle printer, or the left holder of a dual-nozzle printer) or 255 (right holder). cali_idx = -1 to auto-select the best matching profile for the loaded filament.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.tray_id(integer, example0): Absolute tray ID: ams_unit_index × 4 + slot (0–3). External spool: 254 (single-nozzle printer, or the left holder of a dual-nozzle printer) or 255 (right holder).cali_idx(integer, example-1): Calibration profile index to activate. Use -1 for automatic best-match.
Responses
-
200: Success
POST /api/send_ams_control_command
⚠️ Send AMS control command. ?cmd=PAUSE|RESUME|RESET|DONE|ABORT&resume_print=true|false
DONE answers a load prompt's "Filament Extruded, Continue"; ABORT cancels the running filament load or unload. RESUME also resumes the print unless resume_print=false.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.cmd(one of"pause","resume","reset","done","abort", exampleRESUME): AMS control command.resume_print(string): With RESUME, also resume the print (default true). false answers a load prompt.
Responses
-
200: Success
PATCH /api/set_ams_user_setting
Set AMS user setting. ?setting=CALIBRATE_REMAIN_FLAG|STARTUP_READ_OPTION|TRAY_READ_OPTION&enabled=true|false
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.setting(one of"calibrate_remain_flag","startup_read_option","tray_read_option", exampleCALIBRATE_REMAIN_FLAG): AMS user setting to toggle.enabled(boolean, required, exampletrue): Enable or disable the setting.
Responses
-
200: Success
PATCH /api/set_spool_details
Update filament metadata for an AMS slot. ?tray_id=<int>&tray_info_idx=<str>&...
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.tray_id(integer, example1): Absolute tray ID: ams_unit_index × 4 + slot (0–3). External spool: 254 (single-nozzle printer, or the left holder of a dual-nozzle printer) or 255 (right holder).tray_info_idx(string, exampleGFA00): Bambu filament catalog ID (e.g. GFA00). Use 'no_filament' to clear.tray_id_name(string, exampleBambu PLA Basic): Filament brand/product name label.tray_type(string, examplePLA): Filament type string (e.g. PLA, PETG, ABS).tray_color(string, exampleFF0000): Filament color as RRGGBB hex (e.g. FF0000).nozzle_temp_min(integer, example190): Minimum nozzle temperature in °C. Pass -1 to keep existing.nozzle_temp_max(integer, example240): Maximum nozzle temperature in °C. Pass -1 to keep existing.
Responses
-
200: Success
POST /api/set_spool_k_factor
⚠️ Set extrusion calibration k-factor for a spool. (stub — returns success)
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
⚠️ STUB / FIRMWARE WARNING: This route is a no-op that always returns
{"status": "success"} without sending any command to the printer.
The underlying BPM method set_spool_k_factor() carries a docstring
warning ("Broken in recent Bambu firmware") and recommends
select_extrusion_calibration_profile instead. The @deprecated
Python decorator was removed in a later BPM update, but the firmware
limitation stands. Use the select_extrusion_calibration MCP tool
to manage calibration profiles via the supported API.
Responses
200: Success
POST /api/turn_off_ams_dryer
⚠️ Stop the AMS filament dryer. ?ams_id=<int>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
ams_id is the internal chip_id: AMS 2 Pro starts at 0, AMS HT starts at 128. An unknown ams_id or a unit without a dryer is refused with 400 before anything is published.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.ams_id(integer, example0): Internal AMS unit ID (chip_id).
Responses
-
200: Success
POST /api/turn_on_ams_dryer
⚠️ Start the AMS filament dryer. ?ams_id=<int>&target_temp=<int>&duration_hours=<int>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
ams_id is the internal chip_id (not the user-facing unit_id): AMS 2 Pro starts at 0,
AMS HT starts at 128. target_temp defaults to 55°C; duration_hours defaults to 4.
Only an AMS 2 Pro (45–65°C) or AMS HT (45–85°C) is sent the command, for 1–999 hours
(no 24 h cap); anything else is refused with 400 before anything is published, as is a
start while the AMS reports a reason it cannot dry (bpm dryer.refusal_message, e.g.
filament left in the AMS outlet). A start the printer's reply refuses answers 409 with
the decoded reason (bpm dryer.fail_message/dryer.fail_code).
Success needs the unit to report DRYING within 10 s; a unit that was already DRYING and
never reports anything else answers success with confirmed: false.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.ams_id(integer, example0): Internal AMS unit ID (chip_id). AMS 2 Pro starts at 0; AMS HT starts at 128.target_temp(integer, example55): Target drying temperature in °C (default 55).duration_hours(integer, example4): Drying duration in hours (default 4).
Responses
-
200: Success
POST /api/unload_filament
⚠️ Unload the currently loaded filament back to AMS. ?ams_id=<int>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
ams_id is the internal unit id (AMS 2 Pro from 0, AMS HT from 128); without it the
unload names the unit, or external holder (254/255), the active tray is in, or 0 when none is. An unload the
printer's reply refuses within 5 s answers 409 with the decoded reason (bpm's
command_error entry in hms_errors).
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.ams_id(string, required): Internal AMS unit ID (or external holder 254/255) to unload from. Default: the active tray's unit, else 0.
Responses
-
200: Success
Hardware
Nozzle configuration, AI vision detectors, and print option flags.
GET /api/get_detector_settings
Return the current state of all X-Cam AI detector settings on the printer.
Returns enabled/disabled state and sensitivity for each supported detector: spaghetti_detector, buildplate_marker_detector, airprinting_detector, purgechutepileup_detector, nozzleclumping_detector. Also returns the legacy home_flag detectors: nozzle_blob_detect and air_print_detect. Each entry includes 'supported' (bool) indicating hardware support.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: SuccessJSON{ "spaghetti_detector": { "enabled": true, "sensitivity": "medium", "supported": true }, "buildplate_marker_detector": { "enabled": true, "supported": true }, "airprinting_detector": { "enabled": true, "sensitivity": "medium", "supported": true }, "purgechutepileup_detector": { "enabled": true, "sensitivity": "medium", "supported": true }, "nozzleclumping_detector": { "enabled": true, "sensitivity": "medium", "supported": true }, "nozzle_blob_detect": { "enabled": false, "supported": true }, "air_print_detect": { "enabled": false, "supported": true } }
POST /api/refresh_nozzles
⚠️ Trigger nozzle hardware re-read.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
PATCH /api/set_airprinting_detector
Enable/disable air-printing detector. ?enabled=true|false&sensitivity=low|medium|high
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.enabled(boolean, required, exampletrue): Enable or disable the detector.sensitivity(one of"low","medium","high", examplemedium): Detection sensitivity.
Responses
-
200: Success
PATCH /api/set_buildplate_marker_detector
Enable/disable buildplate ArUco marker detector. ?enabled=true|false
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.enabled(boolean, required, exampletrue): Enable or disable the detector.
Responses
-
200: Success
PATCH /api/set_first_layer_inspection
Enable/disable first-layer LiDAR/camera inspection. ?enabled=true|false
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.enabled(boolean, required, exampletrue): Enable or disable first-layer LiDAR/camera inspection.
Responses
-
200: Success
PATCH /api/set_nozzle_details
Set nozzle diameter and type. ?nozzle_diameter=0.2|0.4|0.6|0.8&nozzle_type=BRASS|HARDENED_STEEL|...
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.nozzle_diameter(one of0.2,0.4,0.6,0.8, example0.4): Nozzle diameter in mm.nozzle_type(one of"stainless_steel","hardened_steel","tungsten_carbide","brass","e3d", exampleHARDENED_STEEL): Nozzle material type.
Responses
-
200: Success
PATCH /api/set_nozzleclumping_detector
Enable/disable nozzle clumping detector. ?enabled=true|false&sensitivity=low|medium|high
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.enabled(boolean, required, exampletrue): Enable or disable the detector.sensitivity(one of"low","medium","high", examplemedium): Detection sensitivity.
Responses
-
200: Success
PATCH /api/set_print_option
Set a print option flag. ?option=AUTO_RECOVERY|SOUND_ENABLE|...&enabled=true|false
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.option(one of"auto_recovery","filament_tangle_detect","sound_enable","auto_switch_filament","nozzle_blob_detect","air_print_detect", exampleAUTO_RECOVERY): Print option to toggle.enabled(boolean, required, exampletrue): Enable or disable the option.
Responses
-
200: Success
PATCH /api/set_purgechutepileup_detector
Enable/disable purge chute pile-up detector. ?enabled=true|false&sensitivity=low|medium|high
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.enabled(boolean, required, exampletrue): Enable or disable the detector.sensitivity(one of"low","medium","high", examplemedium): Detection sensitivity.
Responses
-
200: Success
PATCH /api/set_spaghetti_detector
Enable/disable spaghetti detector. ?enabled=true|false&sensitivity=low|medium|high
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.enabled(boolean, required, exampletrue): Enable or disable the detector.sensitivity(one of"low","medium","high", examplemedium): Detection sensitivity.
Responses
-
200: Success
Files
SD card file listing, upload, download, delete, rename, and 3MF project metadata.
DELETE /api/delete_sdcard_file
⚠️ Delete a file or folder from SD card. ?file=<path> (trailing / for folder)
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.file(string, query, example/_jobs/myprint.gcode.3mf): Full SD card path of the file to delete.
Responses
-
200: Success
GET, POST /api/download_file_from_printer
⚠️ Download a file from the printer SD card and return it. ?src=<remote_path>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.src(string, query, example/_jobs/myprint.gcode.3mf): Full SD card path of the file to download.
Responses
-
200: Success
GET /api/get_3mf_props_for_file
Return 3MF project properties for a file on SD card. ?file=<path>&plate=<int>
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.file(string, query, example/_jobs/myprint.gcode.3mf): Full SD card path to the .3mf file.plate(integer, query, example1): Plate number within the .3mf project (1-based). Returns one plate per call — use GET /api/get_all_3mf_props_for_file to fetch every plate in a single call.
Responses
-
200: Success
GET /api/get_all_3mf_props_for_file
Return 3MF project properties for every plate of a file on SD card. ?file=<path>
Same data as /api/get_3mf_props_for_file, one entry per plate, in one call. Plate thumbnail and top-view images are left out unless include_images=true.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.file(string, query, required, example/_jobs/myprint.gcode.3mf): Full SD card path to the .3mf file.include_images(boolean, query, examplefalse): true to include each plate's thumbnail and top-view images as data URIs. Default false.
Responses
-
200: Success
GET /api/get_current_3mf_props
Return 3MF project properties for the currently active print job.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
GET /api/get_file_info
Return the SD card listing entry for one file or folder. ?file=<path>
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.file(string, query, required, example/_jobs/myprint.gcode.3mf): Full SD card path to the file or folder.
Responses
-
200: Success
GET /api/get_sdcard_3mf_files
Return list of .3mf files on SD card.
?cached=true returns the in-memory cached copy without a live FTPS fetch.
Use when stale data is acceptable and low latency matters.
Default (cached=false) performs a live FTPS fetch for up-to-date results, and answers
502 {"status": "error", "reason": "SD card listing failed"} when that fetch failed
(an empty card is a 200 with an empty tree). cached=true never fails: an empty
cache is a JSON null.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
GET /api/get_sdcard_contents
Return full SD card directory listing.
?cached=true returns the in-memory cached copy without a live FTPS fetch.
Use when stale data is acceptable and low latency matters.
Default (cached=false) performs a live FTPS fetch for up-to-date results, and answers
502 {"status": "error", "reason": "SD card listing failed"} when that fetch failed
(an empty card is a 200 with an empty tree). cached=true never fails: an empty
cache is a JSON null.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
POST /api/make_sdcard_directory
⚠️ Create a directory on SD card. ?dir=<path>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.dir(string, example/_jobs/archive): Full SD card path of the directory to create.
Responses
-
200: Success
GET /api/plate_thumbnail
Return a plate's slicer thumbnail from a .3mf on SD card as a JPEG image. ?file=<path>&plate=<int>&quality=preview|standard|full
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.file(string, query, required, example/_jobs/myprint.gcode.3mf): Full SD card path to the .3mf file.plate(integer, query, example1): Plate number within the .3mf project (1-based). Default 1.quality(one of"preview","standard","full", query, examplestandard): Image size tier. Default standard.
Responses
200: Success
GET /api/plate_topview
Return a plate's top-down layout image from a .3mf on SD card as a JPEG image. ?file=<path>&plate=<int>&quality=preview|standard|full
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.file(string, query, required, example/_jobs/myprint.gcode.3mf): Full SD card path to the .3mf file.plate(integer, query, example1): Plate number within the .3mf project (1-based). Default 1.quality(one of"preview","standard","full", query, examplestandard): Image size tier. Default standard.
Responses
200: Success
GET /api/preview_ams_mapping
Resolve, without printing, the ams_mapping print_3mf would send. ?file=<path>&plate=<int>
Uses the spools the printer last reported, as print_3mf does when use_ams=true and no ams_mapping is given. A payload that carries an "error" key is still a preview: it is the reason print_3mf would refuse that plate.
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.file(string, query, required, example/_jobs/myprint.gcode.3mf): Full SD card path to the .3mf file.plate(integer, query, example1): Plate number within the .3mf project (1-based). Default 1.
Responses
200: Success
POST /api/refresh_sdcard_3mf_files
⚠️ Trigger refresh of .3mf file listing from SD card.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Answers 502 {"status": "error", "reason": "SD card listing failed"} when the live
FTPS listing failed, so a failed refresh is never reported as success. An empty card
is a success.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
POST /api/refresh_sdcard_contents
⚠️ Trigger full SD card contents refresh.
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Answers 502 {"status": "error", "reason": "SD card listing failed"} when the live
FTPS listing failed, so a failed refresh is never reported as success. An empty card
is a success.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.
Responses
-
200: Success
PATCH /api/rename_sdcard_file
Rename or move an SD card file. ?src=<path>&dest=<path>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.src(string, example/_jobs/old.gcode.3mf): Current full SD card path.dest(string, example/_jobs/new.gcode.3mf): New full SD card path.
Responses
-
200: Success
GET, POST /api/upload_file_to_host
Upload a file to the local uploads directory. POST multipart/form-data with field 'myFile'.
Responses
-
200: Success
POST /api/upload_file_to_printer
⚠️ Upload a local file to the printer SD card. ?src=<filename_in_uploads>&dest=<remote_path>
⚠️ WRITE OPERATION — requires explicit user confirmation before calling.
Request body (application/x-www-form-urlencoded or application/json)
printer(string, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.src(string, examplemyprint.gcode.3mf): Local filename as uploaded via multipart POST to the host.dest(string, example/_jobs/myprint.gcode.3mf): Destination path on the printer SD card.
Responses
-
200: Success
Camera
Live camera snapshot, active job state report, and stream control.
GET /api/analyze_active_job
Capture the live camera frame and produce a full active job state report.
Returns a cohesive suite of digital assets representing every meaningful dimension of the active print job: project identity, live camera reality, anomaly detection (spaghetti/strand sub-module), print health, and a composite dashboard. All images use the HUD design system (dark palette, verdict badges, zone overlays).
Query parameters:
printer — printer name (required)
store_reference — "true" to store the current frame as the diff baseline
quality — "auto" | "preview" | "standard" | "full"
auto scales with verdict severity (clean=preview,
warning=standard, critical=full)
categories — comma-separated asset category letters to include
(default "X" — composite only):
P = project_thumbnail_png + project_layout_png
C = raw_png + diff_png
D = air_zone_png + mask_png + annotated_png +
heat_png + edge_png
H = health_panel_png
X = job_state_composite_jpg (default primary output)
Response fields (always present):
verdict — "clean" | "warning" | "critical"
anomaly_score — composite anomaly score (0–1)
hot_pct — bright-pixel fraction in air zone
strand_score — directional strand-likelihood (0–1)
diff_score — frame-diff from reference, or null
reference_age_s — age of reference frame in seconds, or null
success_probability — Bayesian print health score (0–1; 1.0 = fully healthy)
decision_confidence — agent's ability to assess failure given current data (0–1)
factor_contributions — dict of 8 Bayesian factor scores for radar chart (0–1 each)
stable_verdict — consensus verdict from recent analysis window, or null
stage — current printer stage code
stage_name — human-readable stage name
stage_gated — true when analysis is skipped due to non-printing stage
layer — current layer number
quality — resolved quality tier used
timestamp — ISO8601 capture time
Error responses:
400 {"error": "no_active_job"} — gcode_state is IDLE/FINISH/FAILED
400 {"error": "no_camera"} — printer has no camera
400 {"error": "not_connected"} — MQTT session not active
Parameters
printer(string, query, required, exampleH2D): Printer name. Omit to use the default printer. Required. Use GET /api/default_printer to resolve the current default.store_reference(boolean, query)quality(string, query, exampleauto)categories(string, query, exampleX)
Responses
-
200: SuccessJSON{ "verdict": "clean", "anomaly_score": 0.04, "hot_pct": 0.03, "strand_score": 0.02, "diff_score": 0.06, "reference_age_s": 312.4, "success_probability": 0.95, "decision_confidence": 0.82, "factor_contributions": { "material": 0.04, "platform": 0.0, "anomaly": 0.026, "thermal": 0.0, "humidity": 0.0, "stability": 0.0, "settings": 0.0, "progress": 0.29 }, "stable_verdict": "clean", "stage": 255, "stage_name": "printing", "stage_gated": false, "layer": 32, "quality": "preview", "timestamp": "2026-03-08T12:00:00", "job_state_composite_jpg": "data:image/jpeg;base64,..." }