X3DStudios

Printability checks

Every model X3D generates or you upload is measured for watertight geometry, thin features, overhangs and build-volume fit before you can order it.

Every model that comes out of /design and every file you upload at /print is measured before it can be ordered. The check is pure geometry: holes in the surface, edges where surfaces branch, zero-area triangles, the smallest and largest overall dimension, the steepest downward-facing face, and the bounding box against the plate. Not every surface runs the whole list — the table below says which check runs where, and an uploaded STL is measured for size and volume only. This page lists what it measures, the exact verdicts it can return, what each one means for your order, and what to do about each failure. One thing none of them does is measure a wall — read the callout below before you rely on the feature-size number.

Where the check runs#

  • On every generation. The finished mesh is validated after repair, and the verdict comes back with the model as printStatus, printabilityScore, issues and recommendations.
  • On every upload. A file sent to /print is parsed for its bounding box, volume and triangle count before you are quoted — an uploaded STL is not inspected for holes, winding or overhangs. A mesh larger than the plate is rejected here.
  • On its own. POST /api/printability measures any STL, GLB or OBJ. No account, no credits, no order.
POST/api/printabilityNo auth

Measure any STL, GLB or OBJ and return a printability report. Free, no key.

What it measures#

CheckHow it is measuredThreshold
Watertight surfaceVertices are welded, then every edge is counted. An edge used by exactly one face is a boundary edge; rings of them are counted as holes and the widest rim is measured.Any boundary edge fails. Zero boundary edges plus at least one triangle is watertight.
Manifold edgesEdges used by three or more faces, where surfaces branch instead of forming one skin.More than zero is a warning.
Face windingThe same directed edge appearing twice — neighbouring faces disagree about which way is out.Any disagreement is a warning.
Degenerate facesTriangles with a repeated vertex index, or an area at or below 1e-9.More than zero is a warning.
Feature sizeThe smallest of the three bounding-box dimensions, in millimetres. An upper bound, not a measured wall.Below 1mm is a warning.
Build-volume fitThe free file check compares the single longest side against 340mm. Upload is stricter and fairer: it sorts the three dimensions largest-first and compares them against the plate sorted the same way, so a long part that fits when rotated is not rejected on one axis.340 x 320 x 340mm (Bambu Lab H2S). Over the plate is a warning on the file check and a hard rejection on upload and again at checkout.
Suspiciously smallLongest side under 5mm. Usually a units problem rather than a tiny part.Warning. STL is millimetres, glTF is read as metres, OBJ as millimetres — but neither glTF nor OBJ declares a unit, so both are flagged unitsAssumed in the report.
OverhangsOn a generation only. Each face normal is measured against the downward print direction and the steepest value is reported in degrees as maxOverhangDeg. Neither the standalone file check nor an upload reports an overhang angle.Reported only. Never blocks — supports are applied at slice time.
The mesh is welded before any of this runs. A binary STL shares no vertices, so an unwelded check calls every valid STL non-watertight.
Wall thickness is not measured
smallestExtentMm on the standalone file report, and minFeatureMm on a generation's printability block, are both the smallest overall dimension of the model. Nothing inside the model can be thicker than that, so it is an honest upper bound — but it cannot see a thin wall inside a large part, which needs a distance field we do not compute. A tall vase with 0.3mm walls reports its overall width and passes. Check real wall thickness in your slicer before ordering something fragile.

Verdicts#

There are two verdict vocabularies because there are two checks. A generation or an upload gets a print status, which decides whether the model can be ordered. The standalone file check returns a report verdict, which decides nothing because no order is attached to it.

On a generation: printStatus#

ValueBadge shownScoreWhat it means for your order
readyPrint Ready95Closed surface, nothing flagged. Orderable.
supportsPrint Ready with Supportswritten by the reviewerPrints, with supports applied automatically. Orderable. Only a reviewed generation returns this: the rule-based reviewer used on text drafts never does.
repairedAuto-Repaired85Holes closed, faces flipped or vertices welded before the model was stored. Orderable.
needs-redesignNeeds Redesign0, 40 or 45Blocked. The model is not marked printable, so it cannot be sent to checkout.

The rule-based reviewer runs on every fast-draft generation from a text prompt, and as the fallback everywhere else. It decides in this order: validation unavailable scores 0, not watertight and not repaired scores 40, a smallest dimension under 0.8mm scores 45, repair applied scores 85, anything else scores 95. Hi-fi, image and multiview generations get a written review instead, which can also return supports and writes its own score.

Multiview responses are not mapped
A multiview generation returns the reviewer's raw status rather than the four values above, so its printStatus can be ready, ready_with_supports, auto_repaired or needs_redesign. If you parse printStatus in code, handle both spellings. A multiview response also stops at printStatus and printDetail — it carries no printabilityScore, issues or recommendations.

On the standalone file check: verdict#

ValueWhenWhat to do
readyNo issues at all.Nothing. Upload it and order.
warningAt least one issue, none of them fatal.Read the issues array. Each one either slices anyway or is fixed automatically at print time.
errorAt least one issue with severity error.Fix it, or let us repair it on print. Exactly one issue is an error today: a surface that is not closed.

What actually blocks an order#

Only two things stop a print reaching checkout, and both return the message verbatim from the cart:

422 — the model did not pass validation
{ "error": "A print in your cart hasn't passed validation and can't be ordered." }
400 — the scaled model is off the plate
{ "error": "A print in your cart exceeds the 340mm build volume." }

The first fires when the stored model has no verified geometry or its status was needs-redesign; ordering a generated model straight from the studio refuses the same case with 422 "This model has not passed server-side validation and cannot be ordered." The second is re-checked at checkout against your chosen scale, so scaling a model up in the cart can push a part that passed at upload back over the plate. Warnings on the standalone file check never block: a non-manifold, badly wound, thin or oversized-but-scalable file still goes through. The exception is thinness on a generation — a generated model whose smallest dimension is under 0.8mm is scored needs-redesign by the rule-based reviewer, and that does block.

Problems, why they matter, and fixes#

ProblemWhy it mattersFix
Holes in the surface (not watertight)A slicer cannot tell inside from outside through a hole, so the file either fails to slice or prints hollow where it should be solid.Closed automatically on print, up to a rim of 256 vertices. At source: export a closed solid rather than a surface model, or repair the mesh before uploading.
Faces pointing inwards (inconsistent winding)Parts of the surface are wound the wrong way round. It renders see-through, and hollowing can cut in the wrong direction.Corrected automatically on print. In your modeller: recalculate normals outward.
Three or more faces on one edge (non-manifold)Surfaces branch rather than forming a single skin. Most slicers cope, but the result around those edges is whatever they guess.Boolean-union the overlapping shells, or separate them into distinct parts. Not repaired automatically.
Zero-area triangles (degenerate)Harmless in themselves, but they inflate the triangle count and confuse some repair tools.Nothing — they are dropped before printing.
Smallest dimension under 1mmThinner than a couple of extrusion widths. The part may not survive being taken off the plate.Scale up, thicken the feature in CAD, or accept that it will be fragile. On the file check and on an upload this never blocks an order; on a generation, a smallest dimension under 0.8mm is scored needs-redesign and does block.
Longest side over 340mmLarger than the build volume. The free check calls it a warning; a mesh over the plate is rejected outright at upload, and rejected again at checkout once your scale is applied.Scale it down, or split it into parts and print them as separate items. Upload compares the three dimensions sorted against 340 x 340 x 320, so rotating the part in your modeller will not change the answer.
Longest side under 5mmDetail smaller than the nozzle width is lost. More often it means the file's units are not what we assumed.Re-export in millimetres, or scale the model up. Neither glTF nor OBJ declares a unit, so the file cannot settle it for us.
Steep overhangsDownward-facing faces need something under them to print onto.Nothing. Supports are applied at slice time and overhangs never block an order. Reorienting the part reduces support scarring if the finish matters.
Validation unavailableThe mesh could not be fetched or parsed, so nothing about it has been verified.Generate or upload again. A model we could not read is never called print-ready — it scores 0.

What we repair automatically#

Repair runs on generated models before they are stored, in this order:

  1. Bake node transforms into the vertices, so a model arriving scaled or rotated by its scene graph is measured at its real size.
  2. Weld duplicate vertices within 0.01mm, then drop degenerate faces.
  3. Flood-fill the winding so neighbouring faces agree, then flip any shell whose signed volume came out negative.
  4. Fill open boundaries with a triangle fan, up to 8 passes, skipping any rim over 256 vertices.
  5. Optionally smooth, then orient and scale the whole model once.
Repair drops textures
Welding and hollowing renumber vertices, which invalidates every non-position attribute: UVs and normals are dropped and material texture references are cleared. That is why a generation also returns previewUrl, the original un-repaired mesh with its textures intact. Print modelUrl, look at previewUrl.

Running the check yourself#

Check a local file
curl -X POST https://x3dstudios.com/api/printability \
  -F "[email protected]"

It reads STL (binary and ASCII), GLB/GLTF and OBJ, detected by the glTF magic bytes first, then the file extension, then by sniffing the contents — so an extensionless upload still works. Anonymous callers get 12 checks an hour and a 25MB limit; a signed-in browser session raises that to 60 an hour and 50MB. The endpoint only ever reads a session cookie, so an API key does not raise the ceiling and a curl like the one above always counts against the anonymous limit. A mesh estimated over 1,500,000 triangles is refused before parsing rather than after. Nothing is stored and no credits are spent. Full request and response reference is at /docs/api/printability.

OBJ is readable here but not orderable
The free check reads OBJ. Print upload does not: /print takes STL, GLB, 3MF and G-code (.gcode or .gcode.3mf), up to 100MB for a mesh and 40MB for a sliced file. Convert an OBJ to STL before ordering.
An already-sliced file skips all of this
Upload a .gcode or .gcode.3mf and we read the footprint, filament and time your slicer wrote into it instead of measuring geometry. The build-volume check is skipped too — your slicer already arranged the plate.