/v1/parts
Create a new part
Create a part from a model file you have uploaded. Uploading takes three calls: get a signed upload URL, send the file bytes to it, then create the part here with the returned path.
Send Authorization: ApiKey <your-api-key> on the Printago API calls (steps 1, 3 and 4). The upload itself (step 2) goes straight to storage and must not carry it.
1. Get a signed upload URL
curl -X POST https://api.printago.io/v1/storage/signed-upload-urls \
-H "Authorization: ApiKey $PRINTAGO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "filenames": ["Benchy.3mf"] }'
Each entry in signedUrls has a path (keep it for step 3), an uploadUrl and the HTTP method to use. The URL expires after 5 minutes.
2. Upload the file bytes
curl -X PUT --upload-file Benchy.3mf \
-H "Content-Type: application/octet-stream" \
"$UPLOAD_URL"
Send the raw file as the request body using the method from step 1 (PUT). Any Content-Type is accepted.
3. Create the part
Call this endpoint with the path from step 1 in fileUris and set type to match the primary file: stl, 3mf, gcode3mf (a 3MF with sliced G-code inside), gcode, step, scad (OpenSCAD), cadquery or build123d. The file is moved from the upload area into the part's permanent storage, and fileUris in the response holds the new location.
4. Wait for processing
After the file is attached, Printago processes it: it reads metadata and filament channels, renders thumbnails, splits 3MF plates and estimates print time. processingStatus reports where that stands:
processingStatus |
Meaning |
|---|---|
"processing" |
Still running. processing.stages shows each step (fetch, read-metadata, thumbnails, split-plates, estimate). |
null |
Ready to print. |
"failed" |
Processing stopped. processing.failedStage and processing.error say where and why. |
By default this request waits for processing to finish (up to roughly 20 seconds) and returns the ready part. Large files can take longer, in which case the part comes back with processingStatus: "processing". Pass ?async=true to return immediately instead. Either way, poll GET /v1/parts/{id} until processingStatus is no longer "processing".
A part cannot be printed until processingStatus is null. POST /v2/builds rejects parts that are still processing or have failed. To retry a failed part, call POST /v1/parts/retry-processing with its ID, or upload a corrected file and PATCH /v1/parts/{id} with the new uploads: path in fileUris.
Query Parameters
Request Body
Part data including file URIs and optional material assignments
Default material assignments per filament channel, keyed by channel index. Each value is a list of { "variantId" }, { "materialId" }, { "groupId" } or { "materialType" } specs. Builds use these unless they override materials.
Files for the part. On create, pass the path values from POST /v1/storage/signed-upload-urls (they start with uploads:). The first file is the primary model and is the one that gets processed. Add more files when the primary file needs them, for example files an OpenSCAD model includes. In responses, these are the files' permanent storage paths.
ID of a process profile to slice this part with instead of the matched printer's profile. Send null to use the printer's profile.
3MF parts only. Slice with the process profile embedded in the 3MF instead of the printer's. When omitted on a 3MF part (and overriddenProcessProfileId is null), it is turned on for you.
Example Request
{
"name": "string",
"type": "scad",
"description": "string",
"fileUris": [
"string"
],
"parameters": [
{
"default": 1,
"options": [
{
"value": "string",
"label": "string"
}
],
"description": "string",
"min": 1,
"max": 1,
"step": 1,
"acceptedContentTypes": [
"string"
],
"multiple": true,
"defaultUri": "string",
"label": "string",
"serialReuse": "share",
"sizing": "stretch",
"name": "string",
"type": "number",
"value": 1
}
],
"printTags": {
"printer.provider": "string",
"printer.id": "string",
"printer.nozzleDiameter": "string",
"printer.modelName": "string",
"filament.type": "string",
"user.tags": "string"
},
"customMetadata": {},
"overriddenProcessProfileId": "string",
"folderId": "string",
"slicerOverride": "string",
"use3MFProcessProfile": true,
"arrangeable": true,
"userTags": [
"string"
],
"assignedMaterials": {}
}
Examples
Create a part from an uploaded 3MF
fileUris takes the path returned by POST /v1/storage/signed-upload-urls, including the uploads: prefix. The response below is the part after processing finished (abbreviated).
{
"name": "Benchy",
"type": "3mf",
"description": "",
"fileUris": [
"uploads:k3m9x2p7q8r4t6v1w5y0z3a2/x4c8v2b6n0m3k7j1h5g9f2d6/Benchy.3mf"
],
"parameters": [],
"printTags": {},
"overriddenProcessProfileId": null
}
{
"id": "p8f2k6m1q9w3e7r5t0y4u2i6",
"storeId": "k3m9x2p7q8r4t6v1w5y0z3a2",
"name": "Benchy",
"type": "3mf",
"description": "",
"fileUris": [
"k3m9x2p7q8r4t6v1w5y0z3a2/parts/p8f2k6m1q9w3e7r5t0y4u2i6/Benchy.3mf"
],
"fileHashes": [
"9f2c4e81a7b3d6f05c8e1a4b7d2f9c3e6a0b5d8f1c4e7a2b9d6f3c0e5a8b1d4f"
],
"thumbnailUri": "k3m9x2p7q8r4t6v1w5y0z3a2/parts/p8f2k6m1q9w3e7r5t0y4u2i6/thumbnail.png",
"parameters": [],
"printTags": {},
"overriddenProcessProfileId": null,
"folderId": null,
"materials": [
{
"index": 0,
"color": "#00AE42",
"type": "PLA"
}
],
"use3MFProcessProfile": true,
"arrangeable": true,
"userTags": [],
"uploadedAt": "2026-09-23T14:02:31.884Z",
"processingStatus": null,
"processing": {
"workflowKey": "p8f2k6m1q9w3e7r5t0y4u2i6-9f2c4e81a7b3d6f0",
"fileHash": "9f2c4e81a7b3d6f05c8e1a4b7d2f9c3e6a0b5d8f1c4e7a2b9d6f3c0e5a8b1d4f",
"attempt": 0,
"startedAt": "2026-09-23T14:02:31.912Z",
"finishedAt": "2026-09-23T14:02:38.407Z",
"stages": [
{
"key": "fetch",
"status": "done"
},
{
"key": "read-metadata",
"status": "done"
},
{
"key": "thumbnails",
"status": "done"
},
{
"key": "split-plates",
"status": "done"
},
{
"key": "estimate",
"status": "done"
}
]
},
"createdAt": "2026-09-23T14:02:31.620Z",
"updatedAt": "2026-09-23T14:02:38.407Z"
}
Create without waiting (`?async=true`)
POST /v1/parts?async=true returns as soon as processing starts. Poll GET /v1/parts/{id} until processingStatus changes.
{
"name": "Cable Clip",
"type": "stl",
"description": "Clip for 6 mm cables",
"fileUris": [
"uploads:k3m9x2p7q8r4t6v1w5y0z3a2/t5r9e3w7q1a4s8d2f6g0h3j7/Cable Clip.stl"
],
"parameters": [],
"printTags": {},
"overriddenProcessProfileId": null,
"userTags": [
"clips"
]
}
{
"id": "c7l1i5p9c3l6i0p4q8w2e5r9",
"storeId": "k3m9x2p7q8r4t6v1w5y0z3a2",
"name": "Cable Clip",
"type": "stl",
"description": "Clip for 6 mm cables",
"fileUris": [
"k3m9x2p7q8r4t6v1w5y0z3a2/parts/c7l1i5p9c3l6i0p4q8w2e5r9/Cable Clip.stl"
],
"thumbnailUri": null,
"userTags": [
"clips"
],
"processingStatus": "processing",
"processing": {
"workflowKey": "c7l1i5p9c3l6i0p4q8w2e5r9-3b7e0a4c8f1d5b9e",
"fileHash": "3b7e0a4c8f1d5b9e2a6c0f4b8d1e5a9c3f7b0d4e8a2c6f9b1d5e0a3c7f4b8d2e",
"attempt": 0,
"startedAt": "2026-09-23T14:11:05.230Z",
"stages": [
{
"key": "fetch",
"status": "pending"
},
{
"key": "read-metadata",
"status": "pending"
},
{
"key": "thumbnails",
"status": "pending"
},
{
"key": "split-plates",
"status": "pending"
},
{
"key": "estimate",
"status": "pending"
}
]
},
"createdAt": "2026-09-23T14:11:04.975Z",
"updatedAt": "2026-09-23T14:11:05.230Z"
}
Response Schema
Files for the part. On create, pass the path values from POST /v1/storage/signed-upload-urls (they start with uploads:). The first file is the primary model and is the one that gets processed. Add more files when the primary file needs them, for example files an OpenSCAD model includes. In responses, these are the files' permanent storage paths.
ID of a process profile to slice this part with instead of the matched printer's profile. Send null to use the printer's profile.
Progress of the most recent processing run: when it started and finished, the status of each stage (pending, running, done, skipped or failed) and, on failure, failedStage and error.
"processing" while the file is being processed, "failed" if processing stopped, and null once the part is ready to print.
3MF parts only. Slice with the process profile embedded in the 3MF instead of the printer's. When omitted on a 3MF part (and overriddenProcessProfileId is null), it is turned on for you.
Example Response
{
"name": "string",
"type": "scad",
"description": "string",
"fileUris": [
"string"
],
"fileHashes": [
"string"
],
"thumbnailUri": "string",
"parameters": [
{
"default": 1,
"options": [
{
"value": "string",
"label": "string"
}
],
"description": "string",
"min": 1,
"max": 1,
"step": 1,
"acceptedContentTypes": [
"string"
],
"multiple": true,
"defaultUri": "string",
"label": "string",
"serialReuse": "share",
"sizing": "stretch",
"name": "string",
"type": "number",
"value": 1
}
],
"printTags": {
"printer.provider": "string",
"printer.id": "string",
"printer.nozzleDiameter": "string",
"printer.modelName": "string",
"filament.type": "string",
"user.tags": "string"
},
"overriddenProcessProfileId": "string",
"folderId": "string",
"materials": [
{
"index": 0,
"color": "string",
"type": "string",
"skip": true,
"name": "string",
"identifier": "string"
}
],
"slicerOverride": "string",
"metadata": {
"printerModel": "string",
"filamentType": [
"string"
],
"filamentColors": [
"string"
],
"filamentDensities": [
1
],
"filamentDiameters": [
1
],
"nozzleDiameter": "string",
"profileCompat": {
"sliced": "string",
"compatible": [
"string"
],
"safe": [
"string"
]
},
"slicerVersion": {
"engine": "orcaslicer",
"version": "string"
},
"plates": [
{
"id": 1,
"name": "string",
"locked": true,
"filamentMapMode": "string",
"filamentMaps": [
1
],
"gcodeFile": "string",
"thumbnailFile": "string",
"thumbnailUploadedUri": "string",
"thumbnailRenderedUri": "string",
"thumbnailNoLightFile": "string",
"thumbnailData": "string",
"topFile": "string",
"pickFile": "string",
"topData": "string",
"pickData": "string",
"topUploadedUri": "string",
"pickUploadedUri": "string",
"patternBboxFile": "string",
"sliced": {
"index": 1,
"extruderType": "string",
"nozzleVolumeType": "string",
"printerModelId": "string",
"nozzleDiameters": "string",
"timelapseType": "string",
"prediction": 1,
"weight": 1,
"outside": true,
"supportUsed": true,
"labelObjectEnabled": true,
"filamentMaps": "string",
"objects": [
{
"identifyId": "string",
"name": "string",
"skipped": true
}
],
"filaments": [
{
"id": "string",
"trayInfoIdx": "string",
"type": "string",
"color": "string",
"usedM": 1,
"usedG": 1
}
],
"warnings": [
{
"msg": "string",
"level": 1,
"errorCode": "string"
}
],
"layerFilamentLists": [
{
"filamentList": 1,
"layerRanges": "string"
}
]
},
"filamentIds": [
"string"
]
}
],
"filamentIds": [
"string"
],
"textSlots": [
{
"key": "string",
"label": "string",
"text": "string",
"volumeCount": 1,
"fontName": "string",
"fontVersion": "string",
"surfaceType": 1,
"occurrences": [
{
"plateId": 1,
"objectId": "string",
"instanceId": 1,
"identifyId": "string"
}
]
}
],
"plateRenderSourceHash": "string"
},
"slicingEstimate": {
"estimatedPrintTimeSeconds": 1,
"materials": [
{
"index": 1,
"usedGrams": 1,
"usedMeters": 1,
"materialType": "string",
"color": "string"
}
],
"totalWeightGrams": 1,
"profiles": {
"machineProfileId": "string",
"machineProfileName": "string",
"processProfileId": "string",
"processProfileName": "string",
"filamentProfileIds": [
"string"
],
"filamentProfileNames": [
"string"
]
},
"slicedAt": "string",
"slicerVersion": "string",
"plates": [
{
"plateId": 1,
"estimatedPrintTimeSeconds": 1,
"materials": [
{
"index": 1,
"usedGrams": 1,
"usedMeters": 1,
"materialType": "string",
"color": "string"
}
],
"totalWeightGrams": 1
}
]
},
"use3MFProcessProfile": true,
"arrangeable": true,
"userTags": [
"string"
],
"gcodePlateUris": {},
"meshUri": "string",
"meshSourceHash": "string",
"meshError": "string",
"uploadedAt": "2025-01-15T12:00:00.000Z",
"customMetadata": {},
"source": "google-drive",
"processingStatus": "processing",
"processing": {
"workflowKey": "string",
"fileHash": "string",
"attempt": 1,
"startedAt": "string",
"finishedAt": "string",
"failedStage": "fetch",
"error": "string",
"stages": [
{
"key": "fetch",
"status": "running",
"attempts": 1,
"error": "string"
}
]
},
"id": "string",
"storeId": "string",
"createdAt": "2025-01-15T12:00:00.000Z",
"updatedAt": "2025-01-15T12:00:00.000Z"
}