Upload a part
Uploads one 3D model and runs the mesh check on it. Send the file itself as the request body (not multipart form data): STL (binary or ASCII, in millimetres) or 3MF. The largest file is the uploads setting's limit (200 MB, GET /v1/upload-limits). The body is streamed to disk as it arrives.
A 3MF file makes one part per printable object in its build (core spec and production extension, including Bambu Studio projects with 3D/Objects/*.model): components and transforms are applied, the model unit is honoured, plates and the arrangement in the file are ignored. Each part is stored as a binary STL and quoted, coloured and priced on its own. Colours and painting in the file are not used: the customer picks one colour per part, and source.colours_ignored says when the file had some. The answer is the first part, as for an STL, plus parts: every part made from the file, linked by source.upload_id. A 3MF is known by its content type (model/3mf), a .3mf file name or its zip signature. It is refused (422) when it cannot be read (unreadable_3mf), when it fails the zip bomb guards in the uploads setting (unsafe_archive), when it has nothing to print (no_mesh_objects) or more objects than one upload may make (too_many_objects).
The file is read and checked off the request thread, so other requests are not held up (contract 0.32.0). At most the uploads setting's check.max_at_once uploads are checked at once; more wait their turn, in order, and the answer comes when the check is done. Reading and checking one part (an STL, or one object of a 3MF) is stopped after check.time_limit_seconds: the upload is refused (422 mesh_check_timed_out, nothing is stored) with a message that names the object and asks for fewer triangles.
The part is stored even when the mesh check rejects it, so it can be priced by hand. mesh_check.issues says what is wrong in words a customer can read, and next_step says what to do: quote, fix_and_upload or request_price. A broken mesh (holes, edges shared by more than two triangles, triangles facing the wrong way) is rejected, not repaired, and points to Request price.
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| Idempotency-Key | string · header required | A unique value per create request, for example a UUID. See Idempotency. |
| filename | string · query | The customer's file name, shown back to them. 1 to 255 characters. |
Request body: the file itself, model/stl, required.
Response fields (11)
| FIELD | TYPE | DESCRIPTION |
|---|---|---|
| id | string required | |
| filename | string or null required | The customer's file name, or for a part made from a 3MF file, the object's name. |
| format | string required | The stored model's format. A part made from a 3MF file is stored as STL too. Always stl. |
| size_bytes | integer required | Size of the stored model. |
| sha256 | string required | SHA-256 of the stored model (the uploaded STL, or the STL made from a 3MF object), hex. |
| created_at | date-time required | |
| mesh_check | MeshCheck required | |
| next_step | enum required | What the customer can do with a part: quote: create a quote (the mesh check passed, perhaps with warnings, or a repaired model passed it). repair: the mesh check rejected it only for defects a repair can fix. Offer "Repair automatically (free)" (POST /v1/parts/{part_id}/repair) and "Manual repair by Fabriken" (a price request with reason: manual_repair, for the fee in GET /v1/price-list). fix_and_upload: fix the model as the issue messages say and upload it again. request_price: the automatic repair could not make it printable (offer the manual repair too), the slicer could not handle it, or the material has no instant price. Show "Request price" so Fabriken can price it by hand. One of quote, repair, fix_and_upload, request_price. |
| repair | PartRepair or null | Null until a repair is asked for (automatic) or made (manual). |
| source | PartSource | Only on a part made from one object of a 3MF file. The parts of one upload share upload_id. |
| parts | array of Part |
Errors (6)
| STATUS | CODE | WHEN |
|---|---|---|
| 400 | material_not_instant | Invalid input, for example a missing Idempotency-Key, an unknown field, an empty upload, or a material without an instant price (material_not_instant). |
| 401 | unauthorized | No API key, or the key is unknown or revoked. Code unauthorized. Send Authorization: Bearer fab_live_... with a valid key. |
| 409 | idempotency_key_in_progress | A request with the same idempotency key is still running. Code idempotency_key_in_progress. Retry shortly. |
| 413 | uploadsfile_too_large | The file is larger than the uploads setting allows. Code file_too_large. |
| 415 | unsupported_media_type | The content type is not a model's. Code unsupported_media_type. |
| 422 | unreadable_3mfunsafe_archiveno_mesh_objectstoo_many_objectsmesh_check_timed_outuploadsidempotency_key_reused | A 3MF file we make no parts from: unreadable_3mf, unsafe_archive, no_mesh_objects or too_many_objects, with a message for the customer. mesh_check_timed_out (contract 0.32.0): one part took longer to read and check than the uploads setting's check.time_limit_seconds; the message names the 3MF object, if one was being read. Or idempotency_key_reused. |
# Send the STL file itself as the body curl "https://api.fabrikenlabs.se/v1/parts?filename=bracket-v3.stl" \ -H "Authorization: Bearer fab_live_••••8f2c" \ -H "Idempotency-Key: 9b1d4e2a-6c3f-4a8e-b5d7-2f0c1e9a8b64" \ -H "Content-Type: model/stl" \ --data-binary @bracket-v3.stl
{
"id": "part_5c2e9a41d07b4f3e8a6c1b2d3e4f5a60",
"filename": "bracket-v3.stl",
"format": "stl",
"size_bytes": <integer>,
"sha256": <string>,
"created_at": "2026-11-06T17:40:58Z",
"mesh_check": {
"status": "ok",
"repairable": false,
"issues": [],
"stats": <MeshStats>
},
"next_step": "quote",
"repair": null
}