Files
Alexey Cluster 7243b88122 Research
Signed-off-by: Alexey Cluster <cluster@cluster.wtf>
2026-10-08 23:33:15 +02:00

12 KiB
Raw Permalink Blame History

6.6. Cloud REST API

HTTPS to Bambu’s cloud (api.bambulab.com / .cn, plus S3/OSS for uploads) — not a TCP service on the printer. The plugin and Studio use it for account and fleet features that sit beside the LAN channels in §6.

This page is the shared transport contract: hosts, mandatory headers, fingerprint headers, and the common response envelope. Per-ABI symbol mappings live under §8.5–§8.15 and §8.10; print pipeline: §8.8.

What it is for

Area Examples
User session Ticket / OAuth login, refresh token, profile
Printer inventory List / bind / rename; plaintext dev_access_code for LAN unlock
Shared app credentials Cert endpoint (app chain / CRL / key blob) for signing, app_cert_install, HTTP PoP
Cloud print Project create, presigned upload, POST /my/task dispatch
Camera remote ttcode / TUTK UID minting (§6.7)
Content / sync User presets, Filament Manager, MakerWorld / Mall, tracking

Does a LAN-Only printer need it?

No for direct device control. SSDP + access code from the printer screen + LAN MQTT / FTPS / :6000 / RTSP is enough to discover the printer, open sessions, upload, and print locally.

Cloud REST is needed for: signing in, fetching access codes without the screen, cloud-dispatched print, shared app-cert fetch (stock secured path), remote TUTK camera, cloud history / MakerWorld, and plugin OTA manifests. A headless LAN-Only setup with a manually entered code never has to call api.bambulab.*.

Regional hosts

Chosen by the user's country_code in app_config (the same switch GUI_App::get_http_url uses for the plugin manifest, see §2.1):

Region API host Web host
US / default https://api.bambulab.com https://bambulab.com
CN https://api.bambulab.cn https://bambulab.cn

Paths in §8 chapters are relative to the regional API host unless noted.

Common request requirements

Requirement Rule
Host Regional API host above (set_country_code / Studio country_code)
Auth Authenticated routes: exactly Authorization: Bearer <access_token>
Body When sending JSON: Content-Type: application/json
TLS Normal public CA trust for api.bambulab.* (not the LAN printer.cer path)

Unauthenticated ticket-login steps mint the bearer; afterwards every cloud REST surface in the networking plugin uses that token unless a chapter says otherwise.

Headers

Authentication
Authorization: Bearer <access_token>
Fingerprint headers

MITM/SSLKEYLOGFILE captures of the stock plugin show the full fingerprint header set on every request:

User-Agent: bambu_network_agent/<agent-ver>
X-BBL-Client-Name: BambuStudio
X-BBL-Client-Type: slicer
X-BBL-Client-Version: <slicer-ver>
X-BBL-Device-ID: <slicer-machine-uuid>
X-BBL-Language: en-US
X-BBL-OS-Type: linux
X-BBL-OS-Version: <os-build, e.g. 10.0.26200>
X-BBL-Agent-Version: <agent-ver>
X-BBL-Executable-info: {}
X-BBL-Agent-OS-Type: linux

X-BBL-Client-Version is the slicer version; X-BBL-Agent-Version is the networking plugin version. X-BBL-OS-Type and X-BBL-Agent-OS-Type both report linux when running through pjarczak's WSL2 bridge; native Linux builds send linux directly. X-BBL-OS-Version carries the OS build string (Windows build number when relayed through pjarczak). X-BBL-Executable-info is the literal string {} on the Linux captures above; stock Windows Studio sends a code-signing JSON for the signed executable instead (subject Shanghai Lunkuo Technology Co., Ltd, GlobalSign EV CodeSigning — #112, mitmproxy 2026-10-05). X-BBL-Device-ID is a machine-local UUID, not the printer serial.

Plus anything Studio injects through bambu_network_set_extra_http_header (§8.10). Direct probes against the production server confirm that none of the X-BBL-* headers, nor even the custom User-Agent, are required for most endpoints (auth, profile, presets, bind, project/upload) — there they influence analytics only.

Exception — POST /v1/user-service/my/task (cloud print / "local print with record"). On live hardware two of these headers are hard requirements; without them the endpoint returns HTTP 403 ("no access rights to the content") and the cloud print never starts:

  • X-BBL-Client-Name: BambuStudio — MakerWorld's user-service authorizes access to the just-uploaded print content only for the stock client identity. Other client-name values are rejected with 403.
  • X-BBL-OS-Type — must match the OS that performed the upload (linux / windows / macos); a mismatch also 403s.

Subtractive bisection against the production server (2026-07) narrowed the requirement to exactly these two X-BBL-* headers; the remaining fingerprint headers stay analytics-only even on /my/task. The signed command-security headers (x-bbl-app-certification-id, x-bbl-device-security-sign) are additionally needed for a secured printer — see §10.5 and §8.8.3.

Exception — POST /v1/iot-service/api/user/ttcode (camera mint). Needs X-BBL-Client-Name: BambuStudio, an X-BBL-OS-Type that matches the app leaf, and the PoP headers (x-bbl-app-certification-id, x-bbl-device-security-sign, §10.5), regardless of the printer's Developer Mode. On a strict account a missing or rejected PoP is HTTP 403 {"code":8,"error":"The specified resource is forbidden. …"} (P2S, 2026-09; X2D, 2026-10). On a lenient account the same call returns 200 and the cloud pushes a cleartext prepare that secured firmware rejects (§10.3). A client without an accepted PoP therefore does not get a usable cloud camera on secured firmware.

Which PoP the cloud accepts is bound to the app leaf's platform, not to the account and not to a release channel (§10.2). Live matrix (one account, 2026-10-05 / 2026-10-06; the same split on a second account in #112):

Leaf X-BBL-OS-Type X-BBL-Executable-info HTTP
Linux a4e8faaa (CN=…-836763c70000) linux {} or the Windows EV JSON 200
Linux a4e8faaa windows / macos {} or the Windows EV JSON 403 code:8
Windows 4a63194e (CN=…-eed1e0410000) windows real EV JSON (as stock Windows sends it) 200
Windows 4a63194e windows {} or absent 403 code:8
Windows 4a63194e linux real EV JSON 403 code:8

X-BBL-Client-Version, X-BBL-Language, X-BBL-Agent-OS-Type and X-BBL-Client-ID do not change the outcome; only X-BBL-OS-Type has to match the leaf, and the Windows leaf additionally requires a non-empty X-BBL-Executable-info. An earlier reading that the Windows leaf was rejected outright came from sending it as linux with X-BBL-Executable-info: {}. Stock release Windows Studio 02.08.02.61 presents the Windows leaf with X-BBL-OS-Type: windows and the EV JSON and gets 200. The cloud is still not equally strict about a missing PoP: some accounts get 403, others get 200 and a cleartext dispatch the printer rejects (§10.3).

TODO: what makes the cloud lenient (200 without an accepted PoP) for some accounts, and whether a macOS leaf exists with the same OS-Type binding.

Device-scoped X-BBL-Client-ID

An additional header X-BBL-Client-ID appears on device-scoped endpoints (e.g. firmware version query, device metadata) but not on account-scoped endpoints (auth, profile, presets):

X-BBL-Client-ID: slicer:{user_id}:{4-char-suffix}

user_id is the numeric user ID (same as uidStr in the profile response). The 4-character suffix appears to be a random or session-derived nonce; its exact derivation is unconfirmed. This header is not required for the API to accept the call.

Common JSON envelope

Most JSON responses share:

{
  "message": "success" | "<human message>",
  "code":    null      | <integer error>,
  "error":   null      | "<string>",
  "...endpoint-specific fields..."
}

code is the "business" error code the GUI inspects (for example 14 for preset quota exceeded, 2 for missing resources). Transport-level failures surface as non-2xx HTTP codes — typically 400 for malformed bodies, 401 for a missing/expired bearer, 422 for invalid-input (e.g. PATCH against an unknown ID), 5xx for server-side failures.

For endpoints that return a plain-text error (notably POST /slicer/setting with a missing mandatory field) the body is a bare string — the envelope is absent.

Where the REST surfaces live

Concern Endpoint(s) Section Evidence
Bearer-token login / refresh / profile / logout POST /v1/user-service/user/ticket/<T>, POST /v1/user-service/user/refreshtoken (body {"refreshToken":…}), GET /v1/user-service/my/profile, POST /v1/user-service/my/logout (body {"refreshtoken":…}, on user_logout(true)) §8.5 (§8.5.1, §8.5.14) MITM + probe
Device account bind / pin bind / unbind Account bind cloud: GET+POST /v1/user-service/my/ticket/<T> (device ticket; LAN mint is TCP :3000 login, §8.6.3); pin: POST /v1/user-service/my/pincode/<PIN> body {"pincode":…}; unbind: DELETE /v1/iot-service/api/user/bind body {"dev_id","force"} §8.6 (§8.6.3–§8.6.5) MITM + LAN pcap (stock 2026-07)
Bind ownership query (query_bind_status) GET /v1/iot-service/api/user/bind_list?dev_ids=<id>[,…] (not /user/bind) §8.6.7 MITM probe (stock_query_bind.mitm)
WebView SSO ticket GET /v1/user-service/user/ticket then POST /v1/user-service/my/ticket/<T> §8.6.6 MITM
Device list / rename (not bind ABI) GET /v1/iot-service/api/user/bind, PATCH /v1/iot-service/api/user/device/info (also GET …/user/print) §8.10 MITM + probe
Printer firmware catalogue stock: GET /v1/iot-service/api/user/device/version?dev_id=<serial> (add X-BBL-Client-ID: slicer:<uid>:<4-char-suffix>) §8.7 SSLKEYLOGFILE (stock)
Cloud print-job pipeline POST /v1/iot-service/api/user/project, PUT <presigned>, …, POST /v1/user-service/my/task §8.8.3 MITM
Generic HTTP ABI (get_user_tasks, …) see per-symbol list §8.10 MITM + probe
User presets sync <m> /v1/iot-service/api/slicer/setting[/<id>]?public=false&version=<bundle> §8.9 MITM + probe
Filament Manager (spool catalogue) ` /v1/design-user-service/my/filament/v2[/batch /ams/sync], GET /v1/design-user-service/filament/config` §8.15
MakerWorld / Mall, OSS upload GET …/design-user-service/my/preference, GET …/design-service/my/design/recommend, GET …/user-service/my/task/<id>, GET/PUT …/comment-service/rating[/inst]/…; GET …/ossconfig?useType=1 + client-signed object PUT §8.12 MITM mw_probe (preference / recommend / rating / my/task); OSS object-key TODO
Camera / live view / HMS snapshot POST /v1/iot-service/api/user/ttcode (liveview mint; PoP required, see above); go-live / get_hms_snapshot endpoints still open §8.11 MITM + live probe (ttcode); go-live / HMS TODO
Analytics / telemetry GET /v1/analysis-st/tag/?UID=… → {tag,secret,iv}; GET /v1/iot-service/api/user/upload?event_track/slicer=<file>.zip.enc → presigned URL; PUT encrypted blob to S3 §8.13 MITM stock_track_cold.mitm

Hub: §6.