Version v1.0.0-alpha (latest)
Modifying Models
Endpoints that take actions on models within a scene.
Scale models#
POST/v1/scene/model/scale/
Set a uniform scale factor on one or more models. scale replaces each model's current scale. It is not multiplied against the model's existing scale.
Scaling a model does not re-validate the layout. A model may end up outside the build volume, which /v1/scene/model/validate/in-bounds/ checks, or overlapping another model, which /v1/scene/model/validate/interferences/ checks.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body does not match the request schema: scene_id or scale missing, a field has the wrong type, or an unknown field is present. The message includes a JSON pointer to the offending field. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. |
Request bodyScaleRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to scale. Omitted or empty means every model in the scene.
scalenumberRequiredThe scale factor to apply. Replaces each model's current scale outright. It is not multiplied against the existing value.
ReturnsModifiedModels
modified_model_idsarray of string (uuid)sRequiredThe selected models the operation was applied to.
The models were scaled. Reports which models were scaled.
/v1/scene/model/scale/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.scale_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
scale=1.5,
)curl -X POST "http://localhost:44388/v1/scene/model/scale/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"scale": 1.5
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.scale_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
scale=0.5,
)curl -X POST "http://localhost:44388/v1/scene/model/scale/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"scale": 0.5
}'{
"modified_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
]
}Orient models#
POST/v1/scene/model/orient/
Set the absolute orientation of one or more models, replacing their current orientation (not a relative rotation). Provide orientation as exactly one of: Euler angles, a 3x3 rotation matrix, or a pair of direction vectors. The response always reports the resulting orientation as Euler angles, regardless of which form was used in the request.
Orienting a model does not re-validate the layout. A model may end up outside the build volume, which /v1/scene/model/validate/in-bounds/ checks, or overlapping another model, which /v1/scene/model/validate/interferences/ checks.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body does not match the request schema: scene_id or orientation missing, orientation matches none of its three shapes, a field has the wrong type, or an unknown field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID or no scene with that id exists, or orientation.linear is not a pure rotation matrix. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. |
Request bodyOrientRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to orient. Omitted or empty means every model in the scene.
orientationEulerAngles or TransformMatrix or DirectionVectorsRequiredA model orientation, given as exactly one of: Euler angles, a transform matrix, or a pair of direction vectors.
Show child attributes
EulerAnglesoption 1 of 3Orientation as Euler angles in degrees. Rotations are applied in the order z, then x, then y.
Show fields
xnumberRequiredRotation in degrees around the x axis (applied second).
ynumberRequiredRotation in degrees around the y axis (applied last).
znumberRequiredRotation in degrees around the z axis (applied first).
TransformMatrixoption 2 of 3Orientation as the linear (rotation/scale) part of a 3x3 transform matrix.
Show fields
lineararray of array of numbersRequiredA 3x3 matrix, given as 3 rows of 3 numbers.
DirectionVectorsoption 3 of 3Orientation as two vectors, in model space, that say which way is "up" and "right" once the model is placed in the scene.
Show fields
z_directionarray of numbersRequiredUnit vector in model space that will point up (+Z) in the scene.
x_directionarray of numbersRequiredUnit vector in model space, perpendicular to z_direction, that will point right (+X) in the scene.
ReturnsModifiedModels
modified_model_idsarray of string (uuid)sRequiredThe selected models the operation was applied to.
The models were oriented. Reports which models were oriented.
/v1/scene/model/orient/from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import EulerAngles
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.orient_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
orientation=EulerAngles(x=0, y=0, z=30),
)curl -X POST "http://localhost:44388/v1/scene/model/orient/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"orientation": {
"x": 0,
"y": 0,
"z": 30
}
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import TransformMatrix
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.orient_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
orientation=TransformMatrix.from_dict({
"linear": [[0.70710678118, -0.70710678118, 0], [0.70710678118, 0.70710678118, 0], [0, 0, 1]],
}),
)curl -X POST "http://localhost:44388/v1/scene/model/orient/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"orientation": {
"linear": [
[
0.70710678118,
-0.70710678118,
0
],
[
0.70710678118,
0.70710678118,
0
],
[
0,
0,
1
]
]
}
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import DirectionVectors
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.orient_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
orientation=DirectionVectors(z_direction=[0, 0, 1], x_direction=[1, 0, 0]),
)curl -X POST "http://localhost:44388/v1/scene/model/orient/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"orientation": {
"z_direction": [
0,
0,
1
],
"x_direction": [
1,
0,
0
]
}
}'{
"modified_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
]
}Translate models#
POST/v1/scene/model/translate/
Move one or more models to a new position, either absolutely (place the model origin at the given coordinates) or relatively (offset each model from where it currently is).
Positions are in the scene's user coordinate space, in millimeters: x/y are horizontal with (0, 0) at the center of the build platform, and z is vertical with 0 at the platform surface. On SLA scenes the z component is ignored. Models stay attached to the build platform (or their raft height).
Translating a model does not re-validate the layout. A model may be moved outside the build volume, which /v1/scene/model/validate/in-bounds/ checks, or into another model, which /v1/scene/model/validate/interferences/ checks.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body does not match the request schema: scene_id or position missing, a field has the wrong type, mode is not one of the documented values, or an unknown field is present. The message includes a JSON pointer to the offending field. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. |
Request bodyTranslateRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to translate. Omitted or empty means every model in the scene.
A position within a printer's build volume, in millimeters, in user coordinate space.
Show child attributes
xnumberRequiredX position. 0 is the center of the build volume. Positive moves right as you face the printer.
ynumberRequiredY position. 0 is the center of the build volume. Positive moves away from you as you face the printer.
znumberRequiredVertical position. 0 is the surface of the build platform.
How to interpret the
positionof a translate request.ABSOLUTE- Set each model's origin to exactly the given coordinates. Translating several models to the same absolute position stacks them.
RELATIVE- Add the given offset to each model's current position, preserving the models' arrangement relative to each other.
ReturnsModifiedModels
modified_model_idsarray of string (uuid)sRequiredThe selected models the operation was applied to.
The models were translated. Reports which models were moved.
/v1/scene/model/translate/from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import ScenePosition, TranslateMode
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.translate_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
position=ScenePosition(x=30, y=-15, z=0),
mode=TranslateMode("ABSOLUTE"),
)curl -X POST "http://localhost:44388/v1/scene/model/translate/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"position": {
"x": 30,
"y": -15,
"z": 0
},
"mode": "ABSOLUTE"
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import ScenePosition, TranslateMode
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.translate_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
position=ScenePosition(x=0, y=25, z=0),
mode=TranslateMode("RELATIVE"),
)curl -X POST "http://localhost:44388/v1/scene/model/translate/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"position": {
"x": 0,
"y": 25,
"z": 0
},
"mode": "RELATIVE"
}'{
"modified_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
]
}Automatically orient models#
POST/v1/scene/model/auto-orient/
Reorients one or more models to the orientation PreForm estimates will produce the best print quality on the scene's printer. Pass mode: "DENTAL" to use the dental-specific algorithm instead, which tilts the appliance's occlusal (biting) plane by tilt degrees (default 0) rather than searching for a quality-optimized orientation.
A reoriented model can end up outside the build volume or overlapping another model. Check with /v1/scene/model/validate/in-bounds/ and /v1/scene/model/validate/interferences/.
This can take a while, especially with the default algorithm across many models, so prefer ?async=true and poll the returned operation.
Unlike /v1/scene/model/scale/ and /v1/scene/model/orient/, an unknown id in model_ids is reported as OPERATION_FAILED rather than NOT_FOUND.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body matches neither request shape: scene_id is missing, mode is present but not "DENTAL", tilt is present without mode: "DENTAL", a field has the wrong type, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 400 | OPERATION_FAILED | A model in model_ids does not exist in the scene, or the auto-orient algorithm itself failed. The message carries the underlying reason. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyobject
Defaultoption 1 of 2Show fields
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to auto-orient. Omitted or empty means every model in the scene.
Dental Modeoption 2 of 2Show fields
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to auto-orient. Omitted or empty means every model in the scene.
Selects a specialized auto-orient algorithm.
DENTAL- Orients for dental appliance printing by tilting the occlusal (biting) plane, rather than searching for a quality-optimized orientation.
tiltintegerDegrees to tilt the occlusal plane. Only applies when
modeis"DENTAL".
Returns 200 OKModifiedModels
modified_model_idsarray of string (uuid)sRequiredThe selected models the operation was applied to.
The models were auto-oriented. Reports which models were reoriented.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
The request was accepted for asynchronous processing (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/auto-orient/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.auto_orient_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/auto-orient/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.auto_orient_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
)curl -X POST "http://localhost:44388/v1/scene/model/auto-orient/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
]
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import AutoOrientMode
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.auto_orient_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
mode=AutoOrientMode("DENTAL"),
tilt=30,
)curl -X POST "http://localhost:44388/v1/scene/model/auto-orient/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"mode": "DENTAL",
"tilt": 30
}'{
"modified_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55"
]
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Generate supports for models#
POST/v1/scene/model/auto-support/
Computes and attaches print supports to one or more models, using the scene's current support settings as defaults for any field omitted from the request. Existing supports on a targeted model are replaced.
Supports and their raft count toward whether a model fits, so a model that fit before can extend past the build volume, or into another model, once it is supported. Check with /v1/scene/model/validate/in-bounds/ and /v1/scene/model/validate/interferences/.
only_minima generates only the touchpoints a model strictly needs to print (a faster, reduced-fidelity pass) instead of the full support structure.
breakaway_structure_enabled is only accepted when the scene's material supports breakaway supports. Otherwise it is rejected with INPUT_ERROR.
This can take a while across many models, so prefer ?async=true and poll the returned operation.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body doesn't match the request schema: scene_id is missing, a numeric field is negative, raft_type is outside its enum, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID or no scene with that id exists, or breakaway_structure_enabled was given but the scene's material doesn't support breakaway supports. |
| 400 | OPERATION_FAILED | A model in model_ids does not exist in the scene, the scene's printer type doesn't support generating supports, a model is hidden/not printable or too large to support, or support generation itself failed. The message carries the underlying reason. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyAutoSupportRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to auto-support. Omitted or empty means every model in the scene.
densitynumberSupport touchpoint density.
slope_multipliernumberScales how aggressively steep overhangs are supported.
touchpoint_size_mmnumberDiameter of each support touchpoint.
raft_thickness_mmnumberThickness of the raft under the supports.
height_above_raft_mmnumberVertical gap left between the raft and the model.
z_compression_correction_mmnumberRaft thickness compensation applied for print compression.
early_layer_merge_mmnumberHeight over which nearby touchpoints are merged into wider bases.
only_minimabooleanGenerate only the touchpoints the model strictly needs to print (a faster, reduced-fidelity pass) instead of the full support structure.
raft_typeenum RaftTypeWhich raft style to build under a model's supports.
FULL_RAFT- A single solid raft under the whole support structure.
MINI_RAFT- A small raft under each touchpoint cluster instead of one solid raft.
MINI_RAFTS_ON_BP- Mini rafts placed directly on the build platform rather than under the touchpoints.
raft_label_enabledbooleanEngrave the model's name on its raft. The text is fixed when the supports are generated: renaming the model afterwards does not change it until the supports are regenerated. Pass
falsefor a raft whose geometry does not depend on the model's name.breakaway_structure_enabledbooleanUse breakaway supports. Only accepted when the scene's material supports this option.
internal_supports_enabledbooleanGenerate internal supports for enclosed cavities.
Returns 200 OKModifiedModels
modified_model_idsarray of string (uuid)sRequiredThe selected models the operation was applied to.
Supports were generated. Reports which models were supported.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
The request was accepted for asynchronous processing (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/auto-support/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.auto_support_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/auto-support/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import RaftType
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.auto_support_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
density=1.2,
raft_type=RaftType("MINI_RAFT"),
only_minima=False,
)curl -X POST "http://localhost:44388/v1/scene/model/auto-support/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"density": 1.2,
"raft_type": "MINI_RAFT",
"only_minima": false
}'{
"modified_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
]
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Rebuild existing supports in place#
POST/v1/scene/model/regenerate-supports/
Rebuilds the support structure of models that already have supports, keeping their touchpoints and the support settings they were generated with. Only what is derived from the model itself changes; in particular a raft generated with raft_label_enabled is re-engraved with the model's current name. This is how to clear the RAFT_LABEL_OUTDATED warning /v1/scene/model/update/ reports after a rename without recomputing touchpoints. To change settings or touchpoints instead, use /v1/scene/model/auto-support/.
Omitting model_ids (or sending an empty array) selects every model in the scene that has supports. Listing a model without supports is an error; nothing is regenerated in that case.
Rebuilt supports and their raft count toward whether a model fits, so a regenerated model can extend past the build volume or into another model. Check with /v1/scene/model/validate/in-bounds/ and /v1/scene/model/validate/interferences/.
Regeneration skips touchpoint finding, the most expensive part of /v1/scene/model/auto-support/, so it is much cheaper than generating supports from scratch. Rebuilding the support structure still takes noticeable time for large models or many models, so prefer ?async=true and poll the returned operation. If any regeneration fails, the whole request fails and no model is changed.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body doesn't match the request schema: scene_id is missing, model_ids is not an array of strings, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, no scene with that id exists, or a model in model_ids has no supports to regenerate. No models were modified. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. No models were modified. |
| 400 | OPERATION_FAILED | Support generation itself failed for a model. No models were modified. The message carries the underlying reason. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyRegenerateSupportsRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models whose supports to regenerate. Omitted or empty means every model in the scene that has supports.
Returns 200 OKModifiedModels
modified_model_idsarray of string (uuid)sRequiredThe selected models the operation was applied to.
The supports were regenerated. Reports which models were regenerated.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
The request was accepted for asynchronous processing (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/regenerate-supports/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.regenerate_supports(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
)curl -X POST "http://localhost:44388/v1/scene/model/regenerate-supports/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
]
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.regenerate_supports(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/regenerate-supports/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'{
"modified_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
]
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Automatically arrange models on the build platform#
POST/v1/scene/model/auto-layout/
Repositions and rotates one or more models to fit them on the build platform. This endpoint is for SLA build platforms. Use /v1/scene/model/auto-pack/ for SLS scenes.
Pass mode: "DENTAL" to temporarily use the scene's dental workspace settings for the layout instead of the standard ones.
By default (on_layout_failure: "FAIL_AND_LEAVE_SCENE_UNCHANGED"), a layout that can't fit every requested model fails the whole request and leaves the scene unchanged. Set on_layout_failure: "EJECT_A_MODEL_AND_RETRY" to instead have the algorithm greedily remove seeded-random models from the scene, retrying until the rest fit (capped by max_ejections, if given). The response reports the outcome only: laid_out_model_ids and ejected_model_ids, in the request's model_ids order. ejected_model_ids is empty unless a model was actually ejected. Fetch /v1/scene/model/list/ afterwards for the resulting poses.
Passing seed makes the chosen arrangement reproducible across calls with the same inputs. If seed is given without layout_attempts/iterations_per_attempt, both are pinned to fixed defaults so results stay deterministic. Without seed, a random seed is used and results vary across calls.
This can take a while, especially with many models or high layout_attempts/iterations_per_attempt, so prefer ?async=true and poll the returned operation.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body doesn't match the request schema: scene_id is missing, mode/on_layout_failure is outside its enum, custom_bounds is missing one of its four fields, seed exceeds the uint32 range, layout_attempts/iterations_per_attempt is outside its range, max_ejections is present without on_layout_failure: "EJECT_A_MODEL_AND_RETRY" or is not a positive integer, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 400 | OPERATION_FAILED | A model in model_ids does not exist in the scene, the scene's printer type doesn't support auto-layout, or (only in FAIL_AND_LEAVE_SCENE_UNCHANGED mode) not every requested model fits within the build volume. The message carries the underlying reason. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyAutoLayoutRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to lay out. Omitted or empty means every model in the scene.
Which workspace's layout parameters to use for this call.
DEFAULT- Use the scene's standard layout parameters.
DENTAL- Temporarily use the scene's dental workspace parameters for this call, then restore the scene's previous parameters afterwards.
model_spacing_mmnumberMinimum gap to leave between models.
allow_overlapping_supportsbooleanAllow one model's supports to intersect another model or its supports.
lock_rotationbooleanOnly translate models and never rotate them, regardless of their individual lock.
custom_boundsLayoutBoundsA custom rectangular region of the build platform to lay out within, in user XY coordinates. All four fields are required together.
Show child attributes
x_min_mmnumberRequiredx_max_mmnumberRequiredy_min_mmnumberRequiredy_max_mmnumberRequired
seedintegerRandom seed controlling the layout algorithm. Given the same models and settings, the same seed reproduces the same arrangement. Omitted means a random seed is used each call.
layout_attemptsintegerNumber of independent layout attempts to try, keeping the best. Defaults to an algorithm-chosen value, or 200 when seed is set without an explicit value (to keep seeded results deterministic).
iterations_per_attemptintegerOptimization iterations per attempt. Defaults to an algorithm-chosen value, or 25000 when seed is set without an explicit value (to keep seeded results deterministic).
on_layout_failureenum OnLayoutFailureWhat to do when the layout cannot fit every requested model.
FAIL_AND_LEAVE_SCENE_UNCHANGED- Fail the request with OPERATION_FAILED. The scene is left unchanged.
EJECT_A_MODEL_AND_RETRY- Greedily remove seeded-random models from the layout and retry until the rest fit, up to max_ejections removals. Ejected models are removed from the scene.
max_ejectionsintegerMaximum number of models that may be ejected before the layout fails outright. Only valid with on_layout_failure: "EJECT_A_MODEL_AND_RETRY". Omitted means no cap.
Returns 200 OKAutoLayoutResponse
laid_out_model_idsarray of string (uuid)sRequiredRequested models that remain in the scene.
ejected_model_idsarray of string (uuid)sRequiredRequested models removed from the scene to make the rest fit. Empty unless on_layout_failure is EJECT_A_MODEL_AND_RETRY.
The models were laid out (and possibly ejected). Reports which requested models remain in the scene and which were ejected.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
The request was accepted for asynchronous processing (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/auto-layout/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.auto_layout_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/auto-layout/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import OnLayoutFailure
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.auto_layout_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10", "a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"],
seed=42,
on_layout_failure=OnLayoutFailure("EJECT_A_MODEL_AND_RETRY"),
max_ejections=1,
)curl -X POST "http://localhost:44388/v1/scene/model/auto-layout/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"
],
"seed": 42,
"on_layout_failure": "EJECT_A_MODEL_AND_RETRY",
"max_ejections": 1
}'{
"laid_out_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"ejected_model_ids": []
}{
"laid_out_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"ejected_model_ids": [
"a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"
]
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Automatically pack models within the build volume#
POST/v1/scene/model/auto-pack/
Packs the selected models within the build volume without duplicating them, using 3D nesting rather than 2D placement. Unselected models are left in place and act as fixed obstacles. This endpoint is for SLS printers. Use /v1/scene/model/auto-layout/ for SLA scenes.
The packer is greedy: it places models one at a time and carries on when one does not fit. on_pack_failure decides what happens to the models it could not place. With FAIL_AND_LEAVE_SCENE_UNCHANGED (the default) the request fails and the scene is left unchanged. With PARK_OUTSIDE_BUILD_VOLUME the packed models keep their new poses, the models that did not fit are parked beside the build volume, outside it, and the response names both groups, so a caller can drain a queue that is larger than one build: pack, keep what landed, carry the rest to the next scene.
The response reports the outcome only: packed_model_ids and unpacked_model_ids covering every selected visible model (hidden models are never packed, so they are not reported). Fetch /v1/scene/model/list/ afterwards for the resulting poses.
This can take a while, so prefer ?async=true and poll the returned operation.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body doesn't match the request schema: scene_id is missing, seed exceeds the uint32 range, distance_from_wall_mm is negative, packing_mode or on_pack_failure is outside its enum, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. |
| 400 | OPERATION_FAILED | The scene's printer type doesn't support packing, or, under on_pack_failure: FAIL_AND_LEAVE_SCENE_UNCHANGED, not every model fits within the build volume. The message carries the underlying reason. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyAutoPackRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to pack. Omitted or empty means every model in the scene.
model_spacing_mmnumberMinimum gap to leave between models. Floored at the material's visual collision offset if smaller.
distance_from_wall_mmnumberMargin to leave between models and the build volume walls.
seedintegerRandom seed controlling the packing algorithm. Omitted means a random seed is used each call.
packing_modeenum PackingModePacking objective used to arrange models within the (SLS) build volume.
PACK_HEIGHT- Minimize the height the packed models occupy.
PACK_VOLUME- Minimize the volume the packed models occupy.
on_pack_failureenum OnPackFailureWhat to do with models the packer could not place inside the build volume.
FAIL_AND_LEAVE_SCENE_UNCHANGED- Fail the request with OPERATION_FAILED. The scene is left unchanged.
PARK_OUTSIDE_BUILD_VOLUME- Keep the pack. Models that fit take their packed poses; models that did not are parked beside the build volume, outside it, and reported in unpacked_model_ids. Nothing is removed from the scene.
Returns 200 OKAutoPackResponse
packed_model_idsarray of string (uuid)sRequiredSelected visible models that ended inside the build volume.
unpacked_model_idsarray of string (uuid)sRequiredSelected visible models the packer could not place. Empty unless on_pack_failure is PARK_OUTSIDE_BUILD_VOLUME.
The pack ran. Reports which of the selected models were placed inside the build volume and which were left outside it. Under FAIL_AND_LEAVE_SCENE_UNCHANGED, unpacked_model_ids is always empty here, because a pack that leaves anything outside fails instead.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
The request was accepted for asynchronous processing (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/auto-pack/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.auto_pack_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/auto-pack/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import OnPackFailure
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.auto_pack_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
on_pack_failure=OnPackFailure("PARK_OUTSIDE_BUILD_VOLUME"),
seed=7,
)curl -X POST "http://localhost:44388/v1/scene/model/auto-pack/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"on_pack_failure": "PARK_OUTSIDE_BUILD_VOLUME",
"seed": 7
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import PackingMode
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.auto_pack_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
packing_mode=PackingMode("PACK_VOLUME"),
distance_from_wall_mm=5,
)curl -X POST "http://localhost:44388/v1/scene/model/auto-pack/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"packing_mode": "PACK_VOLUME",
"distance_from_wall_mm": 5
}'{
"packed_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55"
],
"unpacked_model_ids": []
}{
"packed_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"unpacked_model_ids": [
"9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55"
]
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Fill the build platform with duplicates#
POST/v1/scene/model/fill-build-platform/
Duplicates the selected models and lays out the copies (using the same algorithm as /v1/scene/model/auto-layout/), adding as many as fit on the build platform. Unselected models are left in place and act as fixed obstacles. This endpoint is for SLA. Use /v1/scene/model/fill-build-chamber/ for SLS scenes.
Returns new_model_ids. Call /v1/scene/model/list/ afterwards to see the full scene result.
This can take a while, so prefer ?async=true and poll the returned operation.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body doesn't match the request schema: scene_id is missing, layout_attempts/iterations_per_attempt is outside its range, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 400 | OPERATION_FAILED | A model in model_ids does not exist in the scene, the scene's printer type doesn't support this operation, or not every duplicate fits within the build volume. The message carries the underlying reason. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyFillBuildPlatformRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to duplicate. Omitted or empty means every model in the scene.
layout_optionsLayoutOptionsSLA layout parameters shared by the fill-build-platform operation.
Show child attributes
model_spacing_mmnumberMinimum gap to leave between models.
placement_margin_mmnumberMargin to leave between models and the build platform edge.
allow_overlapping_supportsbooleanAllow one model's supports to intersect another model or its supports.
lock_rotationbooleanOnly translate models and never rotate them, regardless of their individual lock.
layout_attemptsintegerNumber of independent layout attempts to try, keeping the best. Defaults to an algorithm-chosen value.
iterations_per_attemptintegerOptimization iterations per attempt. Defaults to an algorithm-chosen value.
Returns 200 OKNewModelIds
new_model_idsarray of string (uuid)sRequiredIds of the newly created models, in no particular order.
Duplicates were added and laid out. Returns the ids of the newly created models. The scene itself is not included.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
The request was accepted for asynchronous processing (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/fill-build-platform/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.fill_build_platform(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/fill-build-platform/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import LayoutOptions
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.fill_build_platform(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
layout_options=LayoutOptions(model_spacing_mm=5),
)curl -X POST "http://localhost:44388/v1/scene/model/fill-build-platform/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"layout_options": {
"model_spacing_mm": 5
}
}'{
"new_model_ids": [
"a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7",
"b2c3d4e5-f6a7-4890-b1c2-d3e4f5a6b7c8"
]
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Fill the build chamber with duplicates#
POST/v1/scene/model/fill-build-chamber/
Duplicates the selected models and packs them (the same algorithm as /v1/scene/model/auto-pack/) up to fill_to_height_mm, in mm above the build platform. Unselected models are left in place and act as fixed obstacles. This endpoint is for SLS. Use /v1/scene/model/fill-build-platform/ for SLA scenes.
Returns new_model_ids. Call /v1/scene/model/list/ afterwards to see the full scene result.
This can take a while, so prefer ?async=true and poll the returned operation.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body doesn't match the request schema: scene_id is missing, fill_to_height_mm is not positive, seed exceeds the uint32 range, distance_from_wall_mm is negative, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. |
| 400 | OPERATION_FAILED | The scene's printer type doesn't support packing, or not every duplicate fits within fill_to_height_mm. The message carries the underlying reason. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyFillBuildChamberRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to duplicate. Omitted or empty means every model in the scene.
packing_optionsPackingOptionsSLS packing parameters shared by the fill-build-chamber operation.
Show child attributes
model_spacing_mmnumberMinimum gap to leave between models. Floored at the material's visual collision offset if smaller.
distance_from_wall_mmnumberMargin to leave between models and the build volume walls.
seedintegerRandom seed controlling the packing algorithm. Omitted means a random seed is used each call.
fill_to_height_mmnumberHeight, in mm above the build platform, to fill up to. Defaults to the scene's full build volume height.
Returns 200 OKNewModelIds
new_model_idsarray of string (uuid)sRequiredIds of the newly created models, in no particular order.
Duplicates were added and packed. Returns the ids of the newly created models. The scene itself is not included.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
The request was accepted for asynchronous processing (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/fill-build-chamber/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.fill_build_chamber(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/fill-build-chamber/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import PackingOptions
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.fill_build_chamber(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
fill_to_height_mm=42,
packing_options=PackingOptions(seed=12345),
)curl -X POST "http://localhost:44388/v1/scene/model/fill-build-chamber/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"fill_to_height_mm": 42,
"packing_options": {
"seed": 12345
}
}'{
"new_model_ids": [
"a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7",
"b2c3d4e5-f6a7-4890-b1c2-d3e4f5a6b7c8"
]
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Import a model#
POST/v1/scene/model/import/
Loads a 3D model file into the scene as a new model. Supported mesh formats are STL, OBJ, and 3MF, and supported CAD formats are STEP, Solidworks, PTC Creo, JT, Catia V5, Inventor, and Rhino. chordal_tolerance_um and angle_tolerance_deg control tessellation and only affect CAD imports. Saved PreForm projects (.form) are not model files and are rejected with INPUT_ERROR; open them as new scenes with /v1/scene/open/.
The model is placed using the file's own coordinates and is never moved away from models it overlaps; check for that with /v1/scene/model/validate/interferences/ after importing. The scene's build-volume and snapping rules apply, so on printers that snap to the build platform the model is dropped onto it, and a model outside the build volume is moved inside. A model too large to fit stays out of bounds; check with /v1/scene/model/validate/in-bounds/. Change its pose afterwards through the dedicated scale/orient/translate endpoints. name overrides the display name that would otherwise be derived from the file name.
Multi-part files import as one model. A CAD or 3MF file that holds several parts is imported as a single combined model unless the request sets
split_multi_model_file: true.
With split_multi_model_file: true, new_models has one entry per created model, in unspecified order. The per-model overrides (name, lock) target a single model, so they are accepted only when the import creates exactly one model. When a split produces several parts the request fails with INPUT_ERROR and nothing is imported; drop the overrides or import without splitting.
The response identifies each new model by model_id but does not describe it. To read the imported models' properties, such as pose, bounding box, and in_bounds, call /v1/scene/model/list/ with the same scene_id and model_ids set to the model_id values from new_models. To read the whole scene instead, call /v1/scene/get/ with the scene_id. Both are POST requests that take their input as a JSON body:
POST /v1/scene/model/list/
{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": ["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"]
}file must be a model file, matched on the extension regardless of case; the extension is checked before the ids.
Import can take a while for large or CAD files, so prefer ?async=true and poll the returned operation.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body doesn't match the request schema: scene_id or file is missing or not a string, repair_behavior/units is outside its enum, or an unrecognized field is present. |
| 400 | INPUT_ERROR | file's extension is not a supported model format (.form files are rejected here; open them with /v1/scene/open/), a per-model override (name, lock) was given and the file produced more than one model (nothing is imported), scene_id is not a well-formed UUID, or no scene with that id exists. |
| 400 | OPERATION_FAILED | The import failed for a reason not covered above: the file does not exist, is corrupt, holds no model, or repair_behavior is ERROR and the model needs repair. |
| 500 | UNKNOWN_FAILURE | An unexpected exception occurred inside PreFormServer. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyImportModelRequest
scene_idstring (uuid)RequiredThe id of the scene to import into.
filestringRequiredFull path to the file to load.
repair_behaviorenum RepairBehaviorWhat to do when an imported file needs mesh repair.
REPAIR- Attempt to repair the model if needed before adding it to the scene.
ERROR- Fail the import with OPERATION_FAILED instead of importing a model that needs repair.
IGNORE- Import the model as-is, without repairing it.
namestringDisplay name for the new model. Defaults to a name derived from the file name.
unitsenum ImportUnitsThe units to interpret the file's coordinates in.
DETECTED- Use the file's own unit metadata when the format carries it (e.g. STEP, 3MF). Otherwise treat the file as millimeters.
MILLIMETERS- Treat the file's coordinates as millimeters, regardless of any unit metadata in the file.
INCHES- Treat the file's coordinates as inches, regardless of any unit metadata in the file.
lockenum LockOrientation and position constraints on a model, respected by automatic layout and packing operations.
FREE- Layout operations may move and rotate the model freely.
LOCKED_XY_FLIP_ENABLED- Layout may move the model and spin it about the vertical (Z) axis, and may also flip it over; it may not tilt it otherwise.
LOCKED_XY_ROTATION_FREE_TRANSLATION- Layout may move the model and spin it about the vertical (Z) axis, but not tilt it.
LOCKED_ROTATION_FREE_TRANSLATION- Layout may move the model but not rotate it at all.
FULLY_LOCKED- Layout leaves the model exactly where it is.
chordal_tolerance_umnumberTessellation chordal tolerance, in micrometers. Only affects CAD file imports.
angle_tolerance_degnumberTessellation angle tolerance, in degrees. Only affects CAD file imports.
split_multi_model_filebooleanWhether a CAD or 3MF file holding several parts is imported as one model per part. Off by default: the parts arrive as a single combined model, and
nameandlockapply to it. No effect on STL or OBJ.
Returns 200 OKImportModelResult
new_modelsarray of ImportedModelsRequiredOne entry per created model, in no particular order. Exactly one entry unless split_multi_model_file split the file into several models. Use each entry's source_part_name / source_part_index, never its array position, to identify which part of the file it came from.
Show child attributes
model_idstring (uuid)RequiredId of the created model.
source_part_namestringThe name of the part this model was created from, as read from the imported file, before any renaming applied by the scene or the request's name override. Parts with no name in the file get a generated name derived from the file name. Use source_part_index when part names cannot be trusted to identify parts.
source_part_indexintegerZero-based position of the source part within the imported file, counting every part in the file (including parts that failed to import). A single-model import reports 0. This is the stable identity for split assemblies whose parts share a name.
The file was imported. new_models has one entry per created model (exactly one unless split_multi_model_file: true split the file into several).
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
The import was accepted for asynchronous processing (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/import/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.import_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
file="C:\\Models\\bracket.stl",
)curl -X POST "http://localhost:44388/v1/scene/model/import/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"file": "C:\\Models\\bracket.stl"
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import ImportUnits, RepairBehavior
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.import_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
file="C:\\Models\\bracket.step",
repair_behavior=RepairBehavior("REPAIR"),
name="bracket-v2",
units=ImportUnits("MILLIMETERS"),
lock="FREE",
chordal_tolerance_um=10,
angle_tolerance_deg=5,
)curl -X POST "http://localhost:44388/v1/scene/model/import/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"file": "C:\\Models\\bracket.step",
"repair_behavior": "REPAIR",
"name": "bracket-v2",
"units": "MILLIMETERS",
"lock": "FREE",
"chordal_tolerance_um": 10,
"angle_tolerance_deg": 5
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.import_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
file="C:\\Models\\assembly.step",
split_multi_model_file=True,
)curl -X POST "http://localhost:44388/v1/scene/model/import/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"file": "C:\\Models\\assembly.step",
"split_multi_model_file": true
}'{
"new_models": [
{
"model_id": "c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"source_part_name": "bracket",
"source_part_index": 0
}
]
}{
"new_models": [
{
"model_id": "c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"source_part_name": "housing",
"source_part_index": 0
},
{
"model_id": "9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55",
"source_part_name": "base plate",
"source_part_index": 1
}
]
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}List models in a scene#
POST/v1/scene/model/list/
Returns the current properties of the models in a scene. Supply the optional model_ids array to restrict the response to those models.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | scene_id is missing, model_ids is present but not an array of strings, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. |
Request bodyListModelsRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to list. Omitted or empty means every model in the scene.
Returnsobject
modelsarray of ModelPropertiesRequiredShow child attributes
idstring (uuid)RequiredUnique identifier of the model within its scene.
namestringRequiredDisplay name used within job preparation.
A position within a printer's build volume, in millimeters, in user coordinate space.
Show child attributes
xnumberRequiredX position. 0 is the center of the build volume. Positive moves right as you face the printer.
ynumberRequiredY position. 0 is the center of the build volume. Positive moves away from you as you face the printer.
znumberRequiredVertical position. 0 is the surface of the build platform.
Orientation as Euler angles in degrees. Rotations are applied in the order z, then x, then y.
Show child attributes
xnumberRequiredRotation in degrees around the x axis (applied second).
ynumberRequiredRotation in degrees around the y axis (applied last).
znumberRequiredRotation in degrees around the z axis (applied first).
scalenumberRequiredScale factor applied to the model.
The measurement units a model was defined in.
MILLIMETERS- Model coordinates are millimeters.
INCHES- Model coordinates are inches.
bounding_boxobjectRequiredAxis-aligned bounding box of the model (including generated geometry such as supports) in scene coordinates. Group containers have an empty bounding box, serialized as all zeros.
Show child attributes
min_cornerScenePositionA position within a printer's build volume, in millimeters, in user coordinate space.
Show child attributes
xnumberRequiredX position. 0 is the center of the build volume. Positive moves right as you face the printer.
ynumberRequiredY position. 0 is the center of the build volume. Positive moves away from you as you face the printer.
znumberRequiredVertical position. 0 is the surface of the build platform.
max_cornerScenePositionA position within a printer's build volume, in millimeters, in user coordinate space.
Show child attributes
xnumberRequiredX position. 0 is the center of the build volume. Positive moves right as you face the printer.
ynumberRequiredY position. 0 is the center of the build volume. Positive moves away from you as you face the printer.
znumberRequiredVertical position. 0 is the surface of the build platform.
original_filestringRequiredPath of the file the model was imported from.
visiblebooleanRequiredWhether the model is currently shown in the scene.
has_supportsbooleanRequiredWhether support structures have been generated for the model.
is_groupbooleanRequiredWhether this entry is a Part Group container rather than a regular printable model. Group containers have zero volume and an empty bounding box.
is_cagebooleanRequiredWhether the group is a cage. Cages are a subset of groups, so
is_cage: trueimpliesis_group: true.in_boundsbooleanRequiredWhether the model lies entirely within the build volume.
raw_mesh_hashstringRequiredHash of the raw mesh data, before any transform.
canonical_model_hashstringRequiredHash of the mesh data together with its position, orientation, and scale.
Orientation and position constraints on a model, respected by automatic layout and packing operations.
FREE- Layout operations may move and rotate the model freely.
LOCKED_XY_FLIP_ENABLED- Layout may move the model and spin it about the vertical (Z) axis, and may also flip it over; it may not tilt it otherwise.
LOCKED_XY_ROTATION_FREE_TRANSLATION- Layout may move the model and spin it about the vertical (Z) axis, but not tilt it.
LOCKED_ROTATION_FREE_TRANSLATION- Layout may move the model but not rotate it at all.
FULLY_LOCKED- Layout leaves the model exactly where it is.
Returns the requested models, in the order given by model_ids. If model_ids is omitted or empty, returns every model in the scene, in a deterministic but otherwise arbitrary order.
/v1/scene/model/list/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.list_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/list/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.list_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
)curl -X POST "http://localhost:44388/v1/scene/model/list/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
]
}'{
"models": [
{
"id": "c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"name": "bracket.stl",
"position": {
"x": 0,
"y": 0,
"z": 5.2
},
"orientation": {
"x": 0,
"y": 0,
"z": 0
},
"scale": 1,
"units": "MILLIMETERS",
"bounding_box": {
"min_corner": {
"x": -17.9,
"y": -32.9,
"z": 0
},
"max_corner": {
"x": 17.9,
"y": 2.9,
"z": 41.7
}
},
"original_file": "C:\\Models\\bracket.stl",
"visible": true,
"has_supports": true,
"is_group": false,
"is_cage": false,
"in_bounds": true,
"raw_mesh_hash": "5f2b0c9a8d3e41f7",
"canonical_model_hash": "9c81f0b2a6d45e37",
"lock": "FREE"
}
]
}Update model metadata#
POST/v1/scene/model/update/
Edits the metadata for one or more models. This includes renaming them, or changing their unit interpretation or layout lock.
Each field given (name, units, lock) applies its single value to every model specified by model_ids. Omitting model_ids applies the changes to every model in the scene.
Changing units rescales the model's geometry, so a model can grow past the build volume or into another model. Check with /v1/scene/model/validate/in-bounds/ and /v1/scene/model/validate/interferences/.
Renaming does not touch a model's supports. A raft generated with raft_label_enabled engraves the name the model had at generation time, so once a model is renamed its raft spells a different name until its supports are regenerated. Every model this request renames whose raft carries such a label is reported in warnings as RAFT_LABEL_OUTDATED; call /v1/scene/model/regenerate-supports/ to re-engrave the raft with the current name, or /v1/scene/model/auto-support/ to generate supports afresh.
PreForm does not record the engraved text, so the warning describes this request, not the raft: renaming a model back to the name its raft was generated with still warns, and a rename to the model's current name does not. It is also only raised where a label exists: supports without a raft, mini rafts on the build platform (MINI_RAFTS_ON_BP) and legacy V01 rafts carry none, whatever the knob says.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body doesn't match the request schema: scene_id is missing, name/units/lock/model_ids has the wrong type, units or lock is outside its enum, a pose field (scale/position/orientation) is present, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. No models were modified. |
Request bodyUpdateModelRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to update. Omitted or empty means every model in the scene.
namestringDisplay name to apply. Applying to more than one model at once gives all of them this same name.
unitsenum UnitsThe measurement units a model was defined in.
MILLIMETERS- Model coordinates are millimeters.
INCHES- Model coordinates are inches.
lockenum LockOrientation and position constraints on a model, respected by automatic layout and packing operations.
FREE- Layout operations may move and rotate the model freely.
LOCKED_XY_FLIP_ENABLED- Layout may move the model and spin it about the vertical (Z) axis, and may also flip it over; it may not tilt it otherwise.
LOCKED_XY_ROTATION_FREE_TRANSLATION- Layout may move the model and spin it about the vertical (Z) axis, but not tilt it.
LOCKED_ROTATION_FREE_TRANSLATION- Layout may move the model but not rotate it at all.
FULLY_LOCKED- Layout leaves the model exactly where it is.
ReturnsUpdateModelResult
modified_model_idsarray of string (uuid)sRequiredThe selected models the update was applied to.
warningsarray of UpdateModelWarningsRequiredShow child attributes
codestringRequiredRAFT_LABEL_OUTDATED- This request renamed the model, and its raft was generated with
raft_label_enabled, so the engraved name no longer matches the rename. Regenerate its supports to update the label.
model_idstring (uuid)RequiredThe model the warning is about.
The models were updated. Reports which models were updated, and any warnings about side effects the update did not perform.
/v1/scene/model/update/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.update_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
name="bracket-final",
)curl -X POST "http://localhost:44388/v1/scene/model/update/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"name": "bracket-final"
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.update_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
lock="FULLY_LOCKED",
)curl -X POST "http://localhost:44388/v1/scene/model/update/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"lock": "FULLY_LOCKED"
}'{
"modified_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"warnings": []
}{
"modified_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"warnings": [
{
"code": "RAFT_LABEL_OUTDATED",
"model_id": "c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
}
]
}Delete models#
DELETE/v1/scene/model/delete/
Removes one or more models from a scene. Models are validated before any are deleted: if any requested model does not exist, nothing is deleted.
Omitting model_ids (or sending an empty array) deletes every model in the scene.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | scene_id is missing, model_ids is present but not an array of strings, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. Nothing was deleted. |
Request bodyDeleteModelRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to delete. Omitted or empty means every model in the scene.
Returnsno body
The models were deleted. The response has no body.
/v1/scene/model/delete/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
preform.delete_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10", "a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"],
)curl -X DELETE "http://localhost:44388/v1/scene/model/delete/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"
]
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
preform.delete_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X DELETE "http://localhost:44388/v1/scene/model/delete/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'No response body.Replace a model with another model already in the scene#
POST/v1/scene/model/replace/
Swaps one model in the scene for another. The model identified by replacement_model_id takes over the pose (position, orientation, scale, units) of the model identified by model_id, and model_id is removed from the scene. The replacement keeps its own id and its own layout lock; set the lock afterwards with /v1/scene/model/update/ if it should match.
If the replaced model had supports, PreForm attempts to recompute them for the replacement's geometry using the replaced model's support settings and contact locations. Any supports the replacement already carried are discarded. warnings in the response reports when the carry-over could not be done completely; the model may then need supports regenerated with /v1/scene/model/auto-support/.
Taking over the replaced model's pose, and any recomputed supports, can leave the replacement outside the build volume or overlapping another model. Check with /v1/scene/model/validate/in-bounds/ and /v1/scene/model/validate/interferences/.
If the replaced model had a user-provided name and the replacement's name was generated from its file, the replacement takes the replaced model's name.
This endpoint never reads a file. To emulate PreForm's desktop "Replace Model" action, which loads a new version of a part from disk, chain two calls: /v1/scene/model/import/ the new file into the same scene, then call this endpoint with model_id set to the old model and replacement_model_id set to the id import returned. The import places the model with the scene's default snapping; the replacement then moves it onto the old model's pose, so where the import put it does not matter. If the replacement fails, the imported model remains in the scene and can be deleted with /v1/scene/model/delete/. Because the pose carries units, a file imported with default units ends up interpreted in the replaced model's units, exactly as the desktop action does.
Support regeneration can take a while, so prefer ?async=true and poll the returned operation. Neither model is changed until the whole replacement has succeeded.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body doesn't match the request schema: scene_id, model_id or replacement_model_id is missing or not a string, or an unrecognized field is present. |
| 400 | INPUT_ERROR | model_id or replacement_model_id is not a well-formed UUID, the two ids are equal, scene_id is not a well-formed UUID, or no scene with that id exists. Ids are checked in that order, before either model is looked up. |
| 404 | NOT_FOUND | model_id or replacement_model_id is well-formed but does not exist in the scene; the message names the missing id. The scene is not changed. |
| 400 | OPERATION_FAILED | The replacement could not be applied: on the replaced model's pose the replacement's geometry would lie outside the build volume or be thinner than one layer, or support regeneration failed outright. The scene is not changed. |
| 500 | UNKNOWN_FAILURE | An unexpected exception occurred inside PreFormServer. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyReplaceModelRequest
scene_idstring (uuid)RequiredThe id of the scene containing both models.
model_idstring (uuid)RequiredThe id of the model to replace. It is removed from the scene.
replacement_model_idstring (uuid)RequiredThe id of the model that takes over
model_id's pose and supports. It keeps this id and its own layout lock. Must differ frommodel_id.
Returns 200 OKReplaceModelResult
warningsarray of enum ReplaceWarningsRequiredNOT_ALL_SUPPORTS_COMPUTED_SUCCESSFULLY- Support generation for the new geometry did not finish. The new model may have no supports.
NOT_ALL_CONTACT_LOCATIONS_APPLIED- Supports were generated, but not every contact location from the original model's supports could be carried over to the new geometry.
The model was replaced: the replacement now has the replaced model's pose, and model_id is gone from the scene. warnings reports any problem carrying the supports over.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
The replacement was accepted for asynchronous processing (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/replace/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.replace_model(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_id="c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
replacement_model_id="a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7",
)curl -X POST "http://localhost:44388/v1/scene/model/replace/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_id": "c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"replacement_model_id": "a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"
}'{
"warnings": []
}{
"warnings": [
"NOT_ALL_CONTACT_LOCATIONS_APPLIED"
]
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Duplicate models#
POST/v1/scene/model/duplicate/
Creates count copies of each selected model, placed alongside the originals. Models are validated before any are duplicated: if any requested model does not exist, nothing is duplicated.
Returns one results entry per selected model, in request order, naming the ids of the copies created from it (in no particular order).
Only models with their own geometry can be duplicated: selecting a group or cage model fails with INPUT_ERROR, and nothing is duplicated.
count is capped at 1000 per selected model to bound how much a single request can add to a scene.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | scene_id or count is missing, or count is not an integer or is outside 1-1000. Also returned when model_ids is present but not an array of strings, or when an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. Also returned when a model in model_ids is a group or cage with no geometry of its own. Nothing was duplicated. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. Nothing was duplicated. |
Request bodyDuplicateModelRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to duplicate. Omitted or empty means every model in the scene.
countintegerRequiredThe number of copies to create of each selected model.
ReturnsDuplicateModelsResult
resultsarray of ModelsCreatedFromSourcesRequiredOne entry per selected model, in request order. Each entry's new_model_ids are the copies created from that model. The source models themselves are unchanged.
Show child attributes
source_model_idstring (uuid)RequiredThe id of the model the new models were created from.
new_model_idsarray of string (uuid)sRequiredIds of the models created from it, in no particular order.
The models were duplicated. results has one entry per selected model, in request order.
/v1/scene/model/duplicate/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.duplicate_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
count=2,
)curl -X POST "http://localhost:44388/v1/scene/model/duplicate/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"count": 2
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.duplicate_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
count=1,
)curl -X POST "http://localhost:44388/v1/scene/model/duplicate/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"count": 1
}'{
"results": [
{
"source_model_id": "c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"new_model_ids": [
"a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7",
"9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55"
]
}
]
}Cage models#
POST/v1/scene/model/cage/
Generates a support cage around the selected models: a bar-lattice frame that encloses them so they can be handled and depowdered as one rigid unit, protecting small or fragile parts from being lost or damaged. Any existing cage or group among the selected models is replaced.
Fields left unspecified fall back to the scene's default cage-generation settings. enable_square_bars: true (the default) gives the bars a rectangular cross-section (bar_width_mm by bar_thickness_mm). false gives them a round cross-section.
Caging is an SLS-only feature. Caging a scene on an unsupported (SLA) machine fails with OPERATION_FAILED.
Unlike the other model-modifying endpoints, this one does not support ?async=true.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | scene_id is missing, model_ids is present but not an array of strings, a bar/distance field is below its minimum, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. Nothing is caged. |
| 400 | OPERATION_FAILED | The scene's machine type does not support part caging, not all models fit within the build volume once the cage is added, or cage generation produced no cage model. The message carries the underlying reason. |
Request bodyCageRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to cage. Omitted or empty means every model in the scene.
cage_labelstringDisplay name for the new cage model. Defaults to an auto-generated name ("Cage 1", "Cage 2", ...).
generate_mesh_labelbooleanEmboss cage_label as a physical label on the cage geometry.
bar_spacing_mmnumberGap left between adjacent cage bars.
bar_thickness_mmnumberThickness of each cage bar.
bar_width_mmnumberWidth of each cage bar.
distance_to_cage_mmnumberGap left between the models and the surrounding cage.
enable_round_edgesbooleanRound off the edges of the cage bars.
enable_square_barsbooleanGive the cage bars a rectangular cross-section rather than a round one.
ReturnsCageResult
cage_model_idstring (uuid)RequiredThe id of the newly created cage model. The selected models are now children of this model.
The cage was created. cage_model_id is the id of the new cage model (is_cage: true, is_group: true in /v1/scene/model/list/). The selected models are now its children.
/v1/scene/model/cage/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.cage_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10", "9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55"],
)curl -X POST "http://localhost:44388/v1/scene/model/cage/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55"
]
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.cage_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
cage_label="Cage - batch 12",
bar_spacing_mm=8,
bar_thickness_mm=2,
bar_width_mm=2,
distance_to_cage_mm=3,
enable_round_edges=True,
enable_square_bars=False,
)curl -X POST "http://localhost:44388/v1/scene/model/cage/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"cage_label": "Cage - batch 12",
"bar_spacing_mm": 8,
"bar_thickness_mm": 2,
"bar_width_mm": 2,
"distance_to_cage_mm": 3,
"enable_round_edges": true,
"enable_square_bars": false
}'{
"cage_model_id": "a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"
}Label models#
POST/v1/scene/model/label/
Embosses or engraves a text label onto the surface of one or more models, at the given position and orientation. position and orientation are absolute build-platform coordinates, and the identical point and orientation are reused for every id in model_ids.
Every model in model_ids must exist in the scene, and the label must apply cleanly to each one at the selected point. Otherwise the request fails and nothing is labeled.
depth_mm is the height the label rises above the surface (application_mode: "EMBOSS", the default) or the depth it is cut into the surface (application_mode: "ENGRAVE").
Labeling can fail for individual characters (e.g. an unsupported glyph) without failing the whole request. That is reported as a warnings entry rather than an error. If every character fails, the request itself fails with OPERATION_FAILED.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | scene_id, orientation, position, label, font_size_mm, or depth_mm is missing or malformed. Also returned when font_size_mm/depth_mm is outside its allowed range, application_mode is outside its enum, model_ids is present but not an array of strings, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | OPERATION_FAILED | A model in model_ids does not exist in the scene (nothing is labeled), or label generation itself failed, e.g. the shared position/orientation don't land on one of the selected models' surfaces. The message carries the underlying reason. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyLabelRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to label. Omitted or empty means every model in the scene.
orientationEulerAngles or TransformMatrix or DirectionVectorsRequiredA model orientation, given as exactly one of: Euler angles, a transform matrix, or a pair of direction vectors.
Show child attributes
EulerAnglesoption 1 of 3Orientation as Euler angles in degrees. Rotations are applied in the order z, then x, then y.
Show fields
xnumberRequiredRotation in degrees around the x axis (applied second).
ynumberRequiredRotation in degrees around the y axis (applied last).
znumberRequiredRotation in degrees around the z axis (applied first).
TransformMatrixoption 2 of 3Orientation as the linear (rotation/scale) part of a 3x3 transform matrix.
Show fields
lineararray of array of numbersRequiredA 3x3 matrix, given as 3 rows of 3 numbers.
DirectionVectorsoption 3 of 3Orientation as two vectors, in model space, that say which way is "up" and "right" once the model is placed in the scene.
Show fields
z_directionarray of numbersRequiredUnit vector in model space that will point up (+Z) in the scene.
x_directionarray of numbersRequiredUnit vector in model space, perpendicular to z_direction, that will point right (+X) in the scene.
A position within a printer's build volume, in millimeters, in user coordinate space.
Show child attributes
xnumberRequiredX position. 0 is the center of the build volume. Positive moves right as you face the printer.
ynumberRequiredY position. 0 is the center of the build volume. Positive moves away from you as you face the printer.
znumberRequiredVertical position. 0 is the surface of the build platform.
labelstringRequiredThe text to render.
application_modeenum LabelApplicationModeHow a label is applied to a model's surface.
EMBOSS- Raise the label above the surface by depth_mm.
ENGRAVE- Cut the label into the surface to a depth of depth_mm.
font_size_mmnumberRequiredLabel character height.
depth_mmnumberRequiredHeight above the surface (EMBOSS) or depth into it (ENGRAVE).
Returns 200 OKLabelResult
warningsarray of stringsRequiredFree-text, non-fatal problems encountered while applying the label (e.g. individual characters that could not be created).
infosarray of stringsRequiredFree-text informational notices, if any.
The label was applied to every selected model. warnings is non-empty if some characters of the label could not be created.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
Labeling started asynchronously (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/label/from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import EulerAngles, ScenePosition
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.label_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
label="Form 4",
orientation=EulerAngles(x=0, y=0, z=0),
position=ScenePosition(x=0, y=0, z=5),
font_size_mm=5,
depth_mm=0.5,
)curl -X POST "http://localhost:44388/v1/scene/model/label/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"label": "Form 4",
"orientation": {
"x": 0,
"y": 0,
"z": 0
},
"position": {
"x": 0,
"y": 0,
"z": 5
},
"font_size_mm": 5,
"depth_mm": 0.5
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import EulerAngles, LabelApplicationMode, ScenePosition
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.label_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
label="SN-0042",
application_mode=LabelApplicationMode("ENGRAVE"),
orientation=EulerAngles(x=0, y=0, z=0),
position=ScenePosition(x=0, y=0, z=5),
font_size_mm=4,
depth_mm=0.3,
)curl -X POST "http://localhost:44388/v1/scene/model/label/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"label": "SN-0042",
"application_mode": "ENGRAVE",
"orientation": {
"x": 0,
"y": 0,
"z": 0
},
"position": {
"x": 0,
"y": 0,
"z": 5
},
"font_size_mm": 4,
"depth_mm": 0.3
}'{
"warnings": [],
"infos": []
}{
"warnings": [
"Partial failure: 2 characters failed to be created"
],
"infos": []
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Drill holes#
POST/v1/scene/model/drill/
Drills one or more drain/vent holes into the selected models at the given positions and orientations, so trapped resin can escape during printing. Set create_plug: true on a hole to also generate a matching plug model that can be reinserted after printing. Each created plug is reported in plugs with its own id and the id of the drilled model it was cut from.
The same set of drain_holes is applied to every selected model. Every model must exist in the scene, or the request fails and nothing is drilled.
Each hole's position and orientation are absolute build-platform coordinates, not positions relative to each model: the identical point and axis are reused for every id in model_ids. Selecting several models only drills a sensible hole on each of them when they actually share that location (e.g. exact duplicates stacked in place). Otherwise the point will miss the surface of most of the selected models.
Problems with an individual hole (the given point is not on the model's surface, the hole is too large for the model, or a requested plug would be too small to print) do not fail the request. They are reported in warnings, identified by the hole's index into drain_holes, and that hole is skipped for that model. This makes an off-surface point far less disruptive here than for /v1/scene/model/label/: the other holes and models still proceed. If nothing was accomplished (every hole was skipped on every selected model and no plug was created), the request fails with OPERATION_FAILED instead, and the scene is left unmodified.
Set depth_mm to "AUTO" to infer the hole depth by searching into the model along the hole's axis. max_search_distance bounds how far that search goes, and is only accepted together with depth_mm: "AUTO".
Drilling does not work on non-manifold models, but does not verify its input either, because that check is not free. If there is any doubt about a model's integrity, check it with /v1/scene/model/validate/manifoldness/ first and repair it with /v1/scene/model/repair/ if it is not manifold. Failure reports repeat this suggestion.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | scene_id is missing, drain_holes is missing or empty, an entry is missing a required field or has the wrong type, an entry sets max_search_distance without depth_mm: "AUTO", model_ids is present but not an array of strings, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID or no scene with that id exists, or a model in model_ids does not exist in the scene. Nothing is drilled. |
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | OPERATION_FAILED | Nothing was accomplished: every hole was skipped on every selected model and no plug was created. The message carries the per-hole reasons and suggests the manifoldness-validation endpoint. The scene was not modified. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyDrillRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to drill. Omitted or empty means every model in the scene.
drain_holesarray of DrainHolesRequiredShow child attributes
orientationEulerAngles or TransformMatrix or DirectionVectorsRequiredA model orientation, given as exactly one of: Euler angles, a transform matrix, or a pair of direction vectors.
Show child attributes
EulerAnglesoption 1 of 3Orientation as Euler angles in degrees. Rotations are applied in the order z, then x, then y.
Show fields
xnumberRequiredRotation in degrees around the x axis (applied second).
ynumberRequiredRotation in degrees around the y axis (applied last).
znumberRequiredRotation in degrees around the z axis (applied first).
TransformMatrixoption 2 of 3Orientation as the linear (rotation/scale) part of a 3x3 transform matrix.
Show fields
lineararray of array of numbersRequiredA 3x3 matrix, given as 3 rows of 3 numbers.
DirectionVectorsoption 3 of 3Orientation as two vectors, in model space, that say which way is "up" and "right" once the model is placed in the scene.
Show fields
z_directionarray of numbersRequiredUnit vector in model space that will point up (+Z) in the scene.
x_directionarray of numbersRequiredUnit vector in model space, perpendicular to z_direction, that will point right (+X) in the scene.
A position within a printer's build volume, in millimeters, in user coordinate space.
Show child attributes
xnumberRequiredX position. 0 is the center of the build volume. Positive moves right as you face the printer.
ynumberRequiredY position. 0 is the center of the build volume. Positive moves away from you as you face the printer.
znumberRequiredVertical position. 0 is the surface of the build platform.
diameter_mmnumberRequiredDiameter of the drilled hole.
depth_mmnumber or stringRequiredFixed hole depth, or the literal string "AUTO" to infer the depth by searching into the model along the hole's axis.
outset_mmnumberHow far the hole extends outward past the model's surface (the reverse of the drilling direction), to cleanly cut the surface edge.
create_plugbooleanAlso generate a removable plug model matching this hole.
max_search_distancenumberUpper bound on the depth search. Only accepted together with depth_mm: "AUTO".
Returns 200 OKDrillResult
plugsarray of PlugCreatedsRequiredOne entry per plug model created for holes with create_plug: true. Empty if no plugs were created.
Show child attributes
plug_model_idstring (uuid)RequiredThe id of the newly created plug model.
drilled_model_idstring (uuid)RequiredThe id of the pre-existing model that was drilled: the model the plug was cut from and fits back into.
warningsarray of stringsRequiredFree-text, non-fatal problems drilling individual holes, identified by their index into the request's drain_holes.
infosarray of stringsRequiredFree-text informational notices, if any.
The drain holes were processed. Always 200, even when some or all requested holes could not be drilled. Check warnings.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
Drilling started asynchronously (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/drill/from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import DrainHole
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.drill_holes(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
drain_holes=[
DrainHole.from_dict({
"orientation": {
"x": 0,
"y": 0,
"z": 0,
},
"position": {
"x": 0,
"y": 0,
"z": 0,
},
"diameter_mm": 3,
"depth_mm": "AUTO",
"outset_mm": 1,
}),
],
)curl -X POST "http://localhost:44388/v1/scene/model/drill/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"drain_holes": [
{
"orientation": {
"x": 0,
"y": 0,
"z": 0
},
"position": {
"x": 0,
"y": 0,
"z": 0
},
"diameter_mm": 3,
"depth_mm": "AUTO",
"outset_mm": 1
}
]
}'from formlabs_sdk import FormlabsClient
from formlabs_preform_api.models import DrainHole
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.drill_holes(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
drain_holes=[
DrainHole.from_dict({
"orientation": {
"x": 0,
"y": 0,
"z": 1,
},
"position": {
"x": 2.67,
"y": 0,
"z": 0.33,
},
"diameter_mm": 3,
"depth_mm": 4,
"outset_mm": 1,
"create_plug": True,
}),
],
)curl -X POST "http://localhost:44388/v1/scene/model/drill/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"drain_holes": [
{
"orientation": {
"x": 0,
"y": 0,
"z": 1
},
"position": {
"x": 2.67,
"y": 0,
"z": 0.33
},
"diameter_mm": 3,
"depth_mm": 4,
"outset_mm": 1,
"create_plug": true
}
]
}'{
"plugs": [],
"warnings": [],
"infos": []
}{
"plugs": [
{
"plug_model_id": "a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7",
"drilled_model_id": "c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
}
],
"warnings": [],
"infos": []
}{
"plugs": [],
"warnings": [
"Drain hole is too large for drain hole [0]"
],
"infos": []
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Hollow models#
POST/v1/scene/model/hollow/
Hollows the selected models, replacing each one's interior with a cavity that leaves a shell of wall_thickness_mm behind. If a model already has a hollow applied, this replaces it with the new parameters rather than hollowing the already-hollow shell again.
feature_size_mm is the smallest feature size the generated inner surface will resolve. Smaller values increase quality at the cost of processing time. Values above 1 are clamped to 1.
accuracy controls how closely hollowing follows the model's true offset surface, with speed as the tradeoff: 1 is the most accurate and slowest, 0 the fastest and coarsest. The default is 0.5.
Hollowing can fail for an individual model without failing the whole request. This is most commonly because wall_thickness_mm is too large for its geometry. That model is left unhollowed, listed in unhollowed_model_ids and explained in warnings, while every other selected model still proceeds. If no selected model could be hollowed, the request fails with OPERATION_FAILED instead, and the scene is left unmodified.
Hollowing does not work on non-manifold models, but does not verify its input either, because that check is not free. If there is any doubt about a model's integrity, check it with /v1/scene/model/validate/manifoldness/ first and repair it with /v1/scene/model/repair/ if it is not manifold. Failure reports repeat this suggestion.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | scene_id is missing, wall_thickness_mm is missing or below its minimum, feature_size_mm or accuracy is outside its allowed range, model_ids is present but not an array of strings, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID or no scene with that id exists, or a model in model_ids does not exist in the scene. Nothing is hollowed. |
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | OPERATION_FAILED | No selected model could be hollowed. The message carries the per-model reasons and suggests the manifoldness-validation endpoint. The scene was not modified. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyHollowRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to hollow. Omitted or empty means every model in the scene.
wall_thickness_mmnumberRequiredThickness of the shell wall left after hollowing.
feature_size_mmnumberSmallest feature size the generated inner surface will resolve. Smaller is higher quality and slower. Values above 1 are silently clamped to 1.
accuracynumberHow closely the inner surface follows the model's true offset surface: 1 is highest quality and slowest, 0 is fastest and coarsest.
Returns 200 OKHollowResult
hollowed_model_idsarray of string (uuid)sRequiredSelected models that were hollowed. Never empty.
unhollowed_model_idsarray of string (uuid)sRequiredSelected models that could not be hollowed and were left unchanged. Each one also has a line in
warnings.warningsarray of stringsRequiredFree-text, non-fatal problems hollowing individual models (e.g. wall_thickness_mm too large for a model's geometry), one line per model in
unhollowed_model_ids, followed by a suggestion to check manifoldness when any model failed.infosarray of stringsRequiredFree-text informational notices, if any.
At least one selected model was hollowed. Reports which selected models were hollowed and which were left unhollowed.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
Hollowing started asynchronously (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/hollow/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.hollow_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
wall_thickness_mm=1.5,
)curl -X POST "http://localhost:44388/v1/scene/model/hollow/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"wall_thickness_mm": 1.5
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.hollow_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
wall_thickness_mm=1.5,
feature_size_mm=0.5,
accuracy=0.8,
)curl -X POST "http://localhost:44388/v1/scene/model/hollow/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"wall_thickness_mm": 1.5,
"feature_size_mm": 0.5,
"accuracy": 0.8
}'{
"hollowed_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"unhollowed_model_ids": [],
"warnings": [],
"infos": []
}{
"hollowed_model_ids": [
"9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55"
],
"unhollowed_model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"warnings": [
"Failed to hollow model with ID: c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"If a model may not be manifold, you can check it with /v1/scene/model/validate/manifoldness/ and repair it with /v1/scene/model/repair/."
],
"infos": []
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Dental-hollow models#
POST/v1/scene/model/dental-hollow/
Cuts each selected model at a plane, extrudes a solid base of extrude_distance_mm downward from the cut to make it printable, and replaces the original model in the scene with the result. Set hollow: true to also hollow that result using dental-specific parameters (drain holes, honeycomb infill) rather than /v1/scene/model/hollow/'s.
This is the API counterpart to Desktop PreForm's "Scan to Model" tool in the Dental Workspace sidebar: it uses similar orient/trim/extrude/hollow processing, but runs it on models already in the scene and replaces its manual cut-plane placement with automatic gumline detection.
Designed and tested for dental arch models (e.g. intraoral or model scans of a full or partial arch). Behavior on other model types is unsupported.
The cut plane is either an absolute height (cutoff_height_mm) or an offset from each model's automatically detected gumline (cutoff_below_gumline_mm, default 0, exactly at the gumline). These two are mutually exclusive.
shell_thickness_mm, wall_thickness_mm, drain_hole_radius_mm, drain_hole_height_ratio, drain_hole_suppression_distance_mm, drain_hole_max_count, and enable_honeycomb_infill only take effect when hollow: true. Supplying any of them while hollow is false (the default) is rejected. wall_thickness_mm here is the thickness of the honeycomb infill walls inside the hollow cavity, not the outer shell, which is shell_thickness_mm.
Every model in model_ids must exist in the scene, or the request fails and nothing is changed. The response returns one results entry per selected model, in request order, naming the id of the processed model created from it. The source models themselves no longer exist in the scene.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | scene_id or extrude_distance_mm is missing, cutoff_height_mm and cutoff_below_gumline_mm are both given, hollow is false (the default) while any hollow-only field (shell_thickness_mm, wall_thickness_mm, drain_hole_radius_mm, drain_hole_height_ratio, drain_hole_suppression_distance_mm, drain_hole_max_count, enable_honeycomb_infill) is given, a field has the wrong type, a numeric field is outside its allowed range, model_ids is present but not an array of strings, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | OPERATION_FAILED | A model in model_ids does not exist in the scene, or the cut/extrude/hollow processing itself failed. Nothing is changed. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyDentalHollowingRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to process. Omitted or empty means every model in the scene.
cutoff_height_mmnumberAbsolute height of the cut plane. Mutually exclusive with cutoff_below_gumline_mm.
cutoff_below_gumline_mmnumberOffset of the cut plane from each model's automatically detected gumline. Only used when cutoff_height_mm is omitted. 0 (the default) cuts exactly at the gumline.
extrude_distance_mmnumberRequiredLength of solid material extruded downward from the cut plane to create a printable base.
hollowbooleanAlso hollow the resulting model.
shell_thickness_mmnumberThickness of the hollow shell wall. Defaults to 2.75 if omitted. Only used when hollow is true. Rejected otherwise.
wall_thickness_mmnumberThickness of the honeycomb infill walls inside the hollow cavity (not the outer shell, which is shell_thickness_mm). Defaults to 1 if omitted. Only used when hollow is true. Rejected otherwise.
drain_hole_radius_mmnumberMinor-axis radius of the drain holes generated in the hollow shell. Defaults to 1.5 if omitted. Only used when hollow is true. Rejected otherwise.
drain_hole_height_rationumberRatio of a drain hole's major axis to drain_hole_radius_mm. 1 (the default) produces a circular hole and larger values elongate it. Only used when hollow is true. Rejected otherwise.
drain_hole_suppression_distance_mmnumberMinimum distance between generated drain holes; candidates closer than this to another hole are dropped. Defaults to 2 if omitted. Only used when hollow is true. Rejected otherwise.
drain_hole_max_countintegerMaximum number of drain holes generated per contour on the bottom of the model. Defaults to 2 if omitted. Only used when hollow is true. Rejected otherwise.
enable_honeycomb_infillbooleanFill the hollow cavity with honeycomb infill. Only used when hollow is true. Rejected otherwise.
enable_smooth_contour_extended_sidesbooleanSmooth contours that are extended down to the build platform when creating the base.
keep_disconnected_mesh_componentsbooleanKeep every disconnected piece of the cut mesh instead of dropping all but the largest. Defaults to the scene's configured default if omitted.
Returns 200 OKDentalHollowingResult
resultsarray of ModelsCreatedFromSourcesRequiredOne entry per selected model, in request order. Each entry's new_model_ids name the processed model created from that model (exactly one today, though the array form leaves room for processing that yields several). The source model no longer exists in the scene.
Show child attributes
source_model_idstring (uuid)RequiredThe id of the model the new models were created from.
new_model_idsarray of string (uuid)sRequiredIds of the models created from it, in no particular order.
warningsarray of stringsRequiredFree-text, non-fatal problems encountered while processing.
infosarray of stringsRequiredFree-text informational notices, if any.
The models were processed. results has one entry per selected model, in request order. The source models no longer exist in the scene.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
Processing started asynchronously (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/dental-hollow/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.dental_hollow_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
cutoff_height_mm=10,
extrude_distance_mm=2.1,
hollow=False,
)curl -X POST "http://localhost:44388/v1/scene/model/dental-hollow/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"cutoff_height_mm": 10,
"extrude_distance_mm": 2.1,
"hollow": false
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.dental_hollow_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
cutoff_below_gumline_mm=3,
extrude_distance_mm=2.1,
hollow=True,
shell_thickness_mm=2.75,
drain_hole_suppression_distance_mm=5,
drain_hole_max_count=10,
)curl -X POST "http://localhost:44388/v1/scene/model/dental-hollow/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
],
"cutoff_below_gumline_mm": 3,
"extrude_distance_mm": 2.1,
"hollow": true,
"shell_thickness_mm": 2.75,
"drain_hole_suppression_distance_mm": 5,
"drain_hole_max_count": 10
}'{
"results": [
{
"source_model_id": "c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"new_model_ids": [
"b7e2b0f4-9a2d-4b3d-8f4a-2e7c1a9d10c2"
]
}
],
"warnings": [],
"infos": []
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Repair models#
POST/v1/scene/model/repair/
Attempt to repair the selected models' meshes: stitch open boundaries, split non-manifold vertices, remove degenerate faces, and fill holes. Each repaired model keeps its id, position, orientation, and metadata. Only its mesh is replaced.
Repair is best-effort: a 200 response means the repair pipeline ran to completion for every selected model, and each model's entry reports the manifold library's verdict on the repaired mesh, the same check as /v1/scene/model/validate/manifoldness/. A model whose entry says is_manifold: false still carries residual defects.
The scene is committed atomically: if repairing any selected model fails, no model's mesh is replaced.
Repair is a slow operation: its worst-case runtime is O(n²) in the number of triangles in the mesh. Prefer ?async=true and poll the returned operation. /v1/scene/model/validate/manifoldness/ is much cheaper (though still not free). When you only suspect a model may be broken, check first and repair only the models that are not manifold.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | The body does not match the request schema: scene_id missing, a field has the wrong type, model_ids is present but not an array of strings, or an unknown field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 400 | INVALID_QUERY_PARAMETER | async is present but not true or false. |
| 400 | OPERATION_FAILED | The repair itself failed for one of the models, or mesh repair is not available in this build. The message carries the underlying reason. The scene was not modified. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. The scene was not modified. |
Query parameters
asyncbooleanWhen
true, returns202 Acceptedimmediately with the operation's id and itsoperation_urlto poll for the result, instead of blocking until the operation completes.
Request bodyRepairModelsRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to repair. Omitted or empty means every model in the scene.
Returns 200 OKobject
per_model_resultsmap of ModelRepairResultRequiredMap of model id to that model's repair result.
Show child attributes
is_manifoldbooleanRequiredThe manifold library's verdict on the repaired mesh, equivalent to a
/v1/scene/model/validate/manifoldness/statusofMANIFOLD.falsemeans the best-effort repair left residual defects in the mesh (it was still replaced in the scene).
Every selected model was repaired and the scene was updated. One entry per selected model, keyed by model id, reporting the manifold library's verdict on the repaired mesh.
Returns 202 AcceptedOperationAccepted
operation_idstring (uuid)RequiredRandomly generated id of the tracked operation.
operation_urlstringRequiredServer-relative URL of this operation's record:
/v1/operation/get/?operation_id=<operation_id>.GETit to follow progress and retrieve the result.messagestringRequiredHuman-readable pointer to where the result will appear. Purely informational. Use
operation_urlprogrammatically.
The repair started asynchronously (?async=true). Poll the operations endpoints with the returned id.
Response headers (202)
LocationstringServer-relative URL of the accepted operation's record, identical to the response body's
operation_url.GETit to follow progress and retrieve the result.
/v1/scene/model/repair/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.repair_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/repair/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.repair_models(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"],
)curl -X POST "http://localhost:44388/v1/scene/model/repair/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10"
]
}'{
"per_model_results": {
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10": {
"is_manifold": true
},
"9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55": {
"is_manifold": false
}
}
}{
"operation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"operation_url": "/v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7",
"message": "Operation accepted. Poll GET /v1/operation/get/?operation_id=7c9e6679-7425-40de-944b-e07fc1f90ae7 until 'status' is SUCCEEDED or FAILED; the endpoint's response will appear in the 'result' field there."
}Split models into separate bodies#
POST/v1/scene/model/split-bodies/
Splits each selected model into one new model per disconnected body in its mesh, removing the original. Each new model is named "<original name> (Body N)" and keeps the original's position and orientation, subject to the printer's default build-platform snap settings (SLA printers snap each new body to the platform in Z, while SLS does not). Supports are not carried over to the new models.
Returns one results entry per selected model, in request order, naming the ids of the bodies split out of it (in no particular order. Each body's name carries its (Body N) index). The source models themselves no longer exist in the scene.
Models are validated before any are split: if any requested model does not exist, nothing is split. Splitting is also all-or-nothing across the whole request: if any selected model has only one connected body (or no valid geometry) and cannot be split, no model in the request is split.
Use /v1/scene/model/body-count/ first to find which models have more than one body.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | scene_id is missing, model_ids is present but not an array of strings, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. Nothing is split. |
| 400 | OPERATION_FAILED | A model in model_ids has only one connected body (or no valid geometry) and cannot be split. Nothing is split. |
Request bodySplitModelBodiesRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to split. Omitted or empty means every model in the scene.
ReturnsSplitModelBodiesResult
resultsarray of ModelsCreatedFromSourcesRequiredOne entry per selected model, in request order. Each entry's new_model_ids are the per-body models split out of that model, in no particular order (each body's name carries its "(Body N)" index). The source model no longer exists in the scene.
Show child attributes
source_model_idstring (uuid)RequiredThe id of the model the new models were created from.
new_model_idsarray of string (uuid)sRequiredIds of the models created from it, in no particular order.
The selected models were split. results has one entry per selected model, in request order. The source models no longer exist in the scene.
/v1/scene/model/split-bodies/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.split_model_bodies(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10", "a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"],
)curl -X POST "http://localhost:44388/v1/scene/model/split-bodies/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"
]
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.split_model_bodies(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/split-bodies/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'{
"results": [
{
"source_model_id": "c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"new_model_ids": [
"a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7",
"9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55"
]
}
]
}Get model body counts#
POST/v1/scene/model/body-count/
Returns the number of disconnected bodies in each selected model's mesh. A body_count greater than 1 means the model consists of several disconnected shells, which is often unintentional, e.g. several parts imported together as one file. Use /v1/scene/model/split-bodies/ to split such a model into one model per body.
Error codes#
| HTTP | error.code | Cause |
|---|---|---|
| 400 | JSON_PARSE_ERROR | The request body is not a valid JSON object. |
| 400 | JSON_SCHEMA_MATCH_ERROR | scene_id is missing, model_ids is present but not an array of strings, or an unrecognized field is present. |
| 400 | INPUT_ERROR | scene_id is not a well-formed UUID, or no scene with that id exists. |
| 404 | NOT_FOUND | A model in model_ids does not exist in the scene. |
Request bodyGetModelBodyCountRequest
scene_idstring (uuid)RequiredThe id of the scene containing the models.
model_idsarray of string (uuid)sIds of the models to inspect. Omitted or empty means every model in the scene.
Returnsobject
per_model_resultsmap of BodyCountResultRequiredMap of model id to that model's body count.
Show child attributes
body_countintegerRequiredNumber of disconnected bodies (connected mesh components) making up this model. 1 for an ordinary single-body model.
One entry per requested model, keyed by model id. If model_ids is omitted or empty, every model in the scene is counted.
/v1/scene/model/body-count/from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.get_model_body_counts(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
model_ids=["c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10", "a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"],
)curl -X POST "http://localhost:44388/v1/scene/model/body-count/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"model_ids": [
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10",
"a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7"
]
}'from formlabs_sdk import FormlabsClient
with FormlabsClient() as client:
preform = client.preform.connect()
result = preform.get_model_body_counts(
scene_id="3fa85f64-5717-4562-b3fc-2c963f66afa6",
)curl -X POST "http://localhost:44388/v1/scene/model/body-count/" \
-H "Content-Type: application/json" \
-d '{
"scene_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}'{
"per_model_results": {
"c2a37eb2-8e4b-4b8e-9f37-1f5f2c1a9d10": {
"body_count": 2
},
"9b1dd8a0-4f6e-45c2-9a3e-77a01f2b6c55": {
"body_count": 1
}
}
}