{
  "openapi": "3.1.0",
  "info": {
    "title": "Bitbybit CAD Cloud API",
    "version": "1.1.1",
    "description": "High-performance parametric CAD model generation API. Supports multi-format output (STEP, GLTF, decomposed mesh), pipeline execution, file-based conversion, and async job processing.",
    "contact": {
      "name": "Bitbybit",
      "url": "https://bitbybit.dev"
    },
    "termsOfService": "https://bitbybit.dev/terms-and-conditions",
    "license": {
      "name": "Proprietary",
      "url": "https://bitbybit.dev/terms-and-conditions"
    }
  },
  "servers": [
    {
      "url": "https://api.bitbybit.dev",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "Health"
        ],
        "summary": "Health - status check",
        "description": "Returns the operational status of the Bitbybit CAD Cloud API, including service health and readiness. Requires no authentication, so it is suitable for uptime monitoring, load balancer probes and deployment smoke tests.",
        "security": [],
        "responses": {
          "200": {
            "description": "Health status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cad/execute": {
      "post": {
        "tags": [
          "CAD Operations"
        ],
        "summary": "CAD - execute a single operation",
        "description": "Runs a single CAD operation by its fully-qualified identifier.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ExecuteBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cad/pipeline": {
      "post": {
        "tags": [
          "CAD Operations"
        ],
        "summary": "CAD - execute a chained pipeline",
        "description": "Runs multiple operations sequentially. Use '$ref:N' in params to reference step N's result.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PipelineBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cad/compound": {
      "post": {
        "tags": [
          "CAD Operations"
        ],
        "summary": "CAD - execute compound (parallel) operations",
        "description": "Runs several independent CAD operations in parallel as one compound task. Each sub-operation is executed on its own and produces its own downloadable result, so use this when the operations do not depend on each other - for a sequence where each step consumes the previous one's output, use the chained pipeline endpoint instead. Returns 202 with a compound task id; poll it and download the per-sub-task manifest when it completes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompoundExecuteBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Compound task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompoundTaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/models": {
      "get": {
        "tags": [
          "Models"
        ],
        "summary": "Models - list available models",
        "description": "Lists every parametric model available for generation, by name. Use the returned names with the definitions and generation endpoints to discover each model's parameter schema and defaults before submitting a job.",
        "responses": {
          "200": {
            "description": "Model name list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/models/definitions": {
      "post": {
        "tags": [
          "Models"
        ],
        "summary": "Models - batch-fetch definitions",
        "description": "Returns parameter schemas and defaults for the requested model names.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DefinitionsBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Model definitions",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelBatchDefinitionsResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/models/{modelName}": {
      "post": {
        "tags": [
          "Models"
        ],
        "summary": "Models - execute a parametric model",
        "description": "Generic endpoint - works for any registered model. See model-specific endpoints for fully typed parameters. Always returns 202 with a task ID for polling.",
        "parameters": [
          {
            "name": "modelName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Model slug (e.g. dragon-cup, phone-nest)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ModelSubmissionBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/models/{modelName}/batch": {
      "post": {
        "tags": [
          "Models"
        ],
        "summary": "Models - generate a batch",
        "description": "Creates a compound task with one sub-task per item. Each item can have different parameters but shares the same output config.",
        "parameters": [
          {
            "name": "modelName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Model slug (e.g. dragon-cup, phone-nest)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchModelSubmissionBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Compound task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompoundTaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/models/{modelName}/params": {
      "get": {
        "tags": [
          "Models"
        ],
        "summary": "Models - get parameter definitions",
        "description": "Returns parameter schema, types, defaults, and limits for the specified model.",
        "parameters": [
          {
            "name": "modelName",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Model slug (e.g. dragon-cup, phone-nest)"
          }
        ],
        "responses": {
          "200": {
            "description": "Model definition",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModelDefinitionResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/models/dragon-cup": {
      "post": {
        "tags": [
          "Models - Dragon Cup"
        ],
        "summary": "Models - build a Dragon Cup",
        "description": "Parametric dragon-scale textured cup with twisted lofted body, voronoi-like skin cells, and configurable shell thickness. All parameters are optional - defaults produce a ready-to-print model.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "params": {
                  "height": 8,
                  "radiusBottom": 4,
                  "radiusTopOffset": 2,
                  "radiusMidOffset": 2,
                  "rotationTopAngle": 20,
                  "rotationMidAngle": 20,
                  "nrSkinCellsVertical": 5,
                  "nrSkinCellsHorizontal": 10,
                  "nrSkinCellDivisionsTop": 1,
                  "nrSkinCellDivisionsBottom": 3,
                  "skinCellOuterHeight": 0.4,
                  "skinCellInnerHeight": 0.3,
                  "skinCellBottomHeight": 0.4,
                  "skinCellTopHeight": 0.4,
                  "thickness": 0.6,
                  "bottomThickness": 1,
                  "precision": 0.01,
                  "rotation": 0,
                  "scale": 1,
                  "origin": [
                    0,
                    0,
                    0
                  ],
                  "direction": [
                    0,
                    1,
                    0
                  ]
                },
                "outputs": {
                  "formats": [
                    "step",
                    "gltf"
                  ]
                }
              },
              "schema": {
                "$ref": "#/components/schemas/DragonCupBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/models/phone-nest": {
      "post": {
        "tags": [
          "Models - Phone Nest"
        ],
        "summary": "Models - build a Phone Nest",
        "description": "Parametric 3D-printable desktop phone holder/stand with a lofted cradle surface, optional ornamental perforations, and a phone mock-up shape. All parameters are optional - defaults produce a ready-to-print model.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "example": {
                "params": {
                  "heightBottom": 5,
                  "heightTop": 16,
                  "widthBack": 25,
                  "widthFront": 10,
                  "length": 16,
                  "backOffset": 6,
                  "thickness": 0.4,
                  "applyOrnaments": false,
                  "filletRadius": 2,
                  "phoneHeight": 16.8,
                  "phoneWidth": 7.8,
                  "phoneThickness": 0.7,
                  "precision": 0.01,
                  "rotation": 0,
                  "scale": 1,
                  "origin": [
                    0,
                    0,
                    0
                  ],
                  "direction": [
                    0,
                    1,
                    0
                  ]
                },
                "outputs": {
                  "formats": [
                    "step",
                    "gltf"
                  ]
                }
              },
              "schema": {
                "$ref": "#/components/schemas/PhoneNestBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/convert/step-to-gltf": {
      "post": {
        "tags": [
          "Convert"
        ],
        "summary": "Convert - STEP to glTF (.glb)",
        "description": "Simple STEP → glTF conversion using a previously uploaded file. Requires the 'convert' API key scope. Returns 202 with a task ID.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StepToGltfBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/convert/step-to-gltf-advanced": {
      "post": {
        "tags": [
          "Convert"
        ],
        "summary": "Convert - STEP to glTF (.glb), advanced options",
        "description": "Full-control STEP → glTF conversion with fine-grained mesh, export, and coordinate options. Requires the 'convert' API key scope. Returns 202 with a task ID.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StepToGltfAdvancedBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/convert/step-to-gltf-with-draco": {
      "post": {
        "tags": [
          "Convert"
        ],
        "summary": "Convert - STEP to Draco-compressed glTF (.glb)",
        "description": "STEP → glTF conversion with Draco geometry compression using a previously uploaded file. Requires the 'convert' API key scope. Returns 202 with a task ID.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StepToGltfWithDracoBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/convert/step-to-gltf-advanced-with-draco": {
      "post": {
        "tags": [
          "Convert"
        ],
        "summary": "Convert - STEP to Draco-compressed glTF (.glb), advanced options",
        "description": "Full-control STEP → glTF conversion with Draco geometry compression. Combines fine-grained mesh, export, and coordinate options with Draco quantization controls. Requires the 'convert' API key scope. Returns 202 with a task ID.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StepToGltfAdvancedWithDracoBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cad/pro/unfold/face-to-flat": {
      "post": {
        "tags": [
          "Sheet Metal"
        ],
        "summary": "Sheet metal - unfold a face to flat geometry",
        "description": "Proprietary sheet-metal unfold: flattens a single developable face (by index) of an uploaded STEP shape. Requires the 'cad' API key scope. Returns 202 with a task ID; the flattened geometry is delivered as the task's output files.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnfoldFaceToFlatBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cad/pro/unfold/face-report": {
      "post": {
        "tags": [
          "Sheet Metal"
        ],
        "summary": "Sheet metal - unfold a face, metrics report",
        "description": "Proprietary sheet-metal analysis: computes the unfold metrics report (developability, area, ...) for a single face of an uploaded STEP shape. Requires the 'cad' API key scope. Returns 202 with a task ID; the JSON report is delivered in the task metadata.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnfoldFaceReportBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cad/pro/unfold/solid-to-flat": {
      "post": {
        "tags": [
          "Sheet Metal"
        ],
        "summary": "Sheet metal - unfold all solids to flat geometry",
        "description": "Proprietary sheet-metal unfold: loads every solid in an uploaded STEP file and flattens each to a flat pattern. Requires the 'cad' API key scope. Returns 202 with a task ID; all flats are combined into one compound output per format.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnfoldSolidToFlatBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cad/pro/unfold/solid-report": {
      "post": {
        "tags": [
          "Sheet Metal"
        ],
        "summary": "Sheet metal - unfold all solids, metrics report",
        "description": "Proprietary sheet-metal analysis: computes per-solid unfold metrics for every solid in an uploaded STEP file. Requires the 'cad' API key scope. Returns 202 with a task ID; the JSON report array is delivered in the task metadata.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnfoldSolidReportBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cad/pro/unfold/solid": {
      "post": {
        "tags": [
          "Sheet Metal"
        ],
        "summary": "Sheet metal - unfold all solids, geometry + report",
        "description": "Proprietary sheet-metal unfold, full workflow: flattens every solid in an uploaded STEP file AND returns per-solid metrics in one call. Requires the 'cad' API key scope. Returns 202 with a task ID; flats are combined into one compound output per format and the per-solid report array is delivered in the task metadata.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnfoldSolidBody"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "Insufficient scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/files/upload": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Files - request an upload URL",
        "description": "Returns a pre-signed URL for uploading a file via PUT.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FileUploadBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Upload URL and file ID",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/files": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Files - list uploaded files",
        "description": "Returns a paginated list of the files uploaded under your API key, newest first, with each file's id, original name, size, upload status and expiry. Use it to find the file id to pass to a conversion endpoint, and to check that an upload through a pre-signed URL actually completed before you reference it.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-based page number. Defaults to the first page.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many files to return per page.",
            "schema": {
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Return only files in this upload state: pending (a pre-signed URL was issued but the upload has not been confirmed), confirmed (the blob is stored and usable), or expired.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "confirmed",
                "expired"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "File list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/files/{fileId}": {
      "get": {
        "tags": [
          "Files"
        ],
        "summary": "Files - get file details",
        "description": "Returns metadata and download URL for a single file.",
        "parameters": [
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "File ID"
          }
        ],
        "responses": {
          "200": {
            "description": "File details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileDetailResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Files"
        ],
        "summary": "Files - delete a file",
        "description": "Permanently deletes an uploaded file and the stored blob behind it. This cannot be undone, and any task result that still points at the blob stops resolving. Files also expire on their own, so call this only when you need the storage released sooner than that.",
        "parameters": [
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "File ID"
          }
        ],
        "responses": {
          "200": {
            "description": "File deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteFileResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/files/{fileId}/confirm": {
      "post": {
        "tags": [
          "Files"
        ],
        "summary": "Files - confirm upload",
        "description": "Confirms that the file was successfully uploaded to the pre-signed URL.",
        "parameters": [
          {
            "name": "fileId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "File ID"
          }
        ],
        "responses": {
          "200": {
            "description": "File confirmed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfirmResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tasks": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "Tasks - list tasks",
        "description": "Returns a paginated list of your tasks, newest first, each with its id, kind, status, creation time and result link when finished. Filter by status to poll only what is still running, or by kind to separate model generation, STEP conversion, pipelines and compound runs. This is the endpoint to build a job dashboard on rather than polling every task id individually.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "required": false,
            "description": "1-based page number. Defaults to the first page.",
            "schema": {
              "type": "integer",
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many tasks to return per page.",
            "schema": {
              "type": "integer",
              "default": 20
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Return only tasks in this state. waiting and queued are not started yet, processing is running, completed has a downloadable result, failed carries an error, cancelled was stopped by you, and expired means the result has been cleaned up.",
            "schema": {
              "type": "string",
              "enum": [
                "waiting",
                "queued",
                "processing",
                "completed",
                "failed",
                "cancelled",
                "expired"
              ]
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "description": "Return only tasks of this kind: cad for a single operation, model for a registered parametric model, convert-simple and convert-advanced for STEP conversions, pipeline for a chained run, compound for a parallel run.",
            "schema": {
              "type": "string",
              "enum": [
                "cad",
                "model",
                "convert-simple",
                "convert-advanced",
                "pipeline",
                "compound"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskListResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tasks/{taskId}": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "Tasks - get task status",
        "description": "Returns the current status and metadata of a task.",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Task ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Task status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskDetailResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Tasks"
        ],
        "summary": "Tasks - cancel a task",
        "description": "Cancels a waiting or queued task. Has no effect on completed/failed tasks.",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Task ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Task cancelled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CancelTaskResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tasks/{taskId}/result": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "Tasks - get result (default format)",
        "description": "Returns a download URL for the task's primary result. For compound tasks, returns a manifest with per-sub-task download links.",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Task ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Result download link (standard) or compound manifest",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskOrCompoundResultResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Task is not yet completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tasks/{taskId}/result/{format}": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "Tasks - get result in a specific format",
        "description": "Returns a download URL for the requested output format (e.g. glb, step, stpz).",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Task ID"
          },
          {
            "name": "format",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Result format (e.g. glb, step, stpz, metadata, decomposed-mesh)"
          }
        ],
        "responses": {
          "200": {
            "description": "Result download link",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskResultResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Task is not yet completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tasks/{taskId}/results": {
      "get": {
        "tags": [
          "Tasks"
        ],
        "summary": "Tasks - get all results",
        "description": "Returns download URLs for every available result format in a single call. Use this instead of making multiple requests to /result/{format}.",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Task ID"
          }
        ],
        "responses": {
          "200": {
            "description": "Download links for all available formats",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskResultsResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Task is not yet completed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tasks/{taskId}/retry": {
      "post": {
        "tags": [
          "Tasks"
        ],
        "summary": "Tasks - retry a failed or cancelled task",
        "description": "Re-queues a failed or cancelled task with the same parameters.",
        "parameters": [
          {
            "name": "taskId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Task ID"
          }
        ],
        "responses": {
          "202": {
            "description": "Task re-queued",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TaskAcceptedResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Task is not in a retryable state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ExecuteBody": {
        "type": "object",
        "properties": {
          "operation": {
            "type": "string",
            "minLength": 1,
            "description": "Fully-qualified CAD operation identifier (e.g. 'occt.shapes.solid.createBox', 'manifold.primitives.cube', 'jscad.booleans.union'). Supports OCCT, Manifold, JSCAD, and vector/math operations."
          },
          "params": {
            "description": "Operation-specific parameters - varies by operation. See model definitions for available parameters."
          }
        },
        "required": [
          "operation"
        ],
        "additionalProperties": false,
        "description": "Execute a single Bitbybit CAD operation. The operation runs asynchronously and returns a task ID for polling."
      },
      "PipelineBody": {
        "type": "object",
        "properties": {
          "steps": {
            "minItems": 1,
            "maxItems": 50,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PipelineAnyStep"
            },
            "description": "Ordered list of CAD operations executed sequentially. Later steps can reference earlier results using '$ref:N' syntax. Supports map and choice control-flow steps."
          },
          "inputFiles": {
            "description": "References to previously uploaded files. Use '$file:N' in step params to inject file contents (optionally '$file:N:<format>:<codec>' to pick a read format and/or decompress - see PipelineStep.params).",
            "maxItems": 5,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InputFileItem"
            }
          },
          "outputs": {
            "description": "Output format options applied to the final pipeline result. Supports json, csv, stl, 3mf in addition to standard formats. If omitted, raw result data is stored.",
            "$ref": "#/components/schemas/PipelineOutputOptions"
          }
        },
        "required": [
          "steps"
        ],
        "additionalProperties": false,
        "description": "Execute a chain of Bitbybit CAD operations sequentially. Supports $ref:N step references, $file:N file inputs, map iteration, and choice conditionals."
      },
      "PipelineAnyStep": {
        "anyOf": [
          {
            "$ref": "#/components/schemas/MapStep"
          },
          {
            "$ref": "#/components/schemas/ChoiceStep"
          },
          {
            "$ref": "#/components/schemas/PipelineStep"
          }
        ],
        "description": "A pipeline step - either a plain operation, a map (iteration), or a choice (conditional)."
      },
      "MapStep": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "const": "map",
            "description": "Declares this step as a map (iteration) step"
          },
          "items": {
            "description": "Source array to iterate over. Typically a '$ref:N' reference to a previous step that produced an array."
          },
          "steps": {
            "minItems": 1,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PipelineStep"
            },
            "description": "Sub-steps to execute for each item. Use '$item' for the current element, '$index' for the iteration number."
          },
          "reduce": {
            "description": "Optional sub-steps to combine map results. Use '$mapResult' to reference the collected array of iteration outputs.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PipelineStep"
            }
          }
        },
        "required": [
          "type",
          "items",
          "steps"
        ],
        "description": "Iterate over an array, executing sub-steps for each element. Produces an array of results (or a reduced value)."
      },
      "PipelineStep": {
        "type": "object",
        "properties": {
          "operation": {
            "type": "string",
            "minLength": 1,
            "description": "Fully-qualified CAD operation identifier (e.g. 'occt.shapes.solid.createSphere'). Supports OCCT, Manifold, JSCAD, and vector/math operations."
          },
          "params": {
            "description": "Operation parameters. Use '$ref:N' (e.g. '$ref:0') to reference the output of a previous step by its zero-based index. Use '$file:N[:<format>][:<codec>]' to reference an input file's contents - <format> is one of text|buffer|uint8|json|base64 (default text), and the optional <codec> is one of raw|gunzip|auto (default raw) to decompress the file first; 'auto' gunzips only when gzip magic bytes are present, so '$file:0:text:auto' reads both plain and gzip-compressed uploads (e.g. .step and .stpz)."
          },
          "output": {
            "description": "When true, this step's result is included in a separate result.json output alongside any shape files.",
            "type": "boolean"
          }
        },
        "required": [
          "operation",
          "params"
        ],
        "description": "A single step in a sequential CAD pipeline. Steps can reference outputs of earlier steps via $ref or input files via $file."
      },
      "ChoiceStep": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "const": "choice",
            "description": "Declares this step as a choice (conditional) step"
          },
          "value": {
            "description": "Value to compare. Typically a '$ref:N' reference to a previous step result."
          },
          "operator": {
            "description": "Comparison operator to apply between value and compareTo",
            "$ref": "#/components/schemas/ComparisonOperator"
          },
          "compareTo": {
            "description": "Value to compare against. Not required for 'exists' operator."
          },
          "then": {
            "minItems": 1,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PipelineStep"
            },
            "description": "Sub-steps to execute when condition is true"
          },
          "else": {
            "description": "Sub-steps to execute when condition is false. If omitted, produces null.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PipelineStep"
            }
          }
        },
        "required": [
          "type",
          "value",
          "operator",
          "then"
        ],
        "description": "Conditional execution - evaluate a condition and run the matching branch."
      },
      "ComparisonOperator": {
        "type": "string",
        "enum": [
          "eq",
          "neq",
          "gt",
          "gte",
          "lt",
          "lte",
          "exists"
        ],
        "description": "Comparison operator: 'eq' (equal), 'neq' (not equal), 'gt' (greater than), 'gte' (greater or equal), 'lt' (less than), 'lte' (less or equal), 'exists' (value is not null/undefined)."
      },
      "InputFileItem": {
        "type": "object",
        "properties": {
          "fileId": {
            "type": "string",
            "minLength": 1,
            "description": "ID of a previously uploaded file (from POST /files/upload)"
          },
          "role": {
            "type": "string",
            "minLength": 1,
            "description": "Role identifier for the file (e.g. 'step-model', 'coordinates-csv')"
          }
        },
        "required": [
          "fileId",
          "role"
        ],
        "additionalProperties": false,
        "description": "Reference to an uploaded file to be used as input"
      },
      "PipelineOutputOptions": {
        "type": "object",
        "properties": {
          "formats": {
            "minItems": 1,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PipelineOutputFormat"
            },
            "description": "Output formats to generate. Supports standard CAD formats (step, stpz, gltf, decomposed-mesh) plus pipeline-specific formats (json, csv, stl, 3mf)."
          },
          "meshPrecision": {
            "description": "Tessellation precision for mesh-based outputs (decomposed-mesh and gltf). Lower values produce denser meshes.",
            "$ref": "#/components/schemas/MeshPrecision"
          },
          "gltfMeshPrecision": {
            "description": "Override tessellation precision specifically for glTF output. When set, takes precedence over meshPrecision for glTF only.",
            "$ref": "#/components/schemas/MeshPrecision"
          },
          "adjustYtoZ": {
            "description": "Convert from Y-up to Z-up coordinate system in the output. Useful for software that expects Z-up.",
            "type": "boolean"
          },
          "includePipelineInMetadata": {
            "description": "Include the full pipeline definition in metadata.json. Defaults to true. Set to false to exclude it for smaller metadata.",
            "type": "boolean"
          }
        },
        "required": [
          "formats"
        ],
        "additionalProperties": false,
        "description": "Controls which output formats are generated for pipeline results. Supports additional formats (json, csv, stl, 3mf) beyond standard model outputs."
      },
      "PipelineOutputFormat": {
        "type": "string",
        "enum": [
          "step",
          "stpz",
          "decomposed-mesh",
          "gltf",
          "json",
          "csv",
          "stl",
          "3mf"
        ],
        "description": "Pipeline output file format. Includes all standard formats plus: 'json' (JSON data), 'csv' (CSV text), 'stl' (binary STL mesh - Manifold/JSCAD only), '3mf' (3MF mesh package - Manifold/JSCAD only)."
      },
      "MeshPrecision": {
        "type": "number",
        "minimum": 0.0001,
        "maximum": 10,
        "description": "Mesh tessellation precision (lower = finer). Range: [0.0001, 10]",
        "example": 0.1
      },
      "CompoundExecuteBody": {
        "type": "object",
        "properties": {
          "parallel": {
            "type": "boolean",
            "const": true,
            "description": "Must be true - signals that items should be processed concurrently"
          },
          "items": {
            "minItems": 1,
            "maxItems": 100,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompoundItem"
            },
            "description": "List of independent CAD operations to execute in parallel. Each item becomes a sub-task."
          }
        },
        "required": [
          "parallel",
          "items"
        ],
        "additionalProperties": false,
        "description": "Execute multiple independent Bitbybit CAD operations in parallel. Each item runs as a separate sub-task and can be polled individually."
      },
      "CompoundItem": {
        "type": "object",
        "properties": {
          "operation": {
            "type": "string",
            "minLength": 1,
            "description": "Fully-qualified CAD operation identifier. Supports OCCT, Manifold, JSCAD, and vector/math operations."
          },
          "params": {
            "description": "Operation-specific parameters for this sub-task"
          },
          "inputFiles": {
            "description": "References to previously uploaded files to be used as inputs for this operation",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InputFileItem"
            }
          },
          "outputFormats": {
            "description": "Per-item output format overrides. If omitted, the task produces raw result data.",
            "maxItems": 10,
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            }
          }
        },
        "required": [
          "operation"
        ],
        "additionalProperties": false,
        "description": "A single operation within a compound (parallel) task. Each item runs independently."
      },
      "DefinitionsBody": {
        "type": "object",
        "properties": {
          "names": {
            "minItems": 1,
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            },
            "description": "Array of model slugs to fetch definitions for (e.g. ['dragon-cup', 'phone-nest'])"
          }
        },
        "required": [
          "names"
        ],
        "additionalProperties": false,
        "description": "Fetch parameter definitions for one or more parametric models. Returns available parameters, types, defaults, and constraints."
      },
      "ModelSubmissionBody": {
        "type": "object",
        "properties": {
          "params": {
            "description": "Model-specific parameters. Omitted params use their defaults. See model definition for available options.",
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {}
          },
          "outputs": {
            "$ref": "#/components/schemas/OutputOptions"
          }
        },
        "required": [
          "outputs"
        ],
        "description": "Submit a parametric model for generation with specified parameters and output formats"
      },
      "OutputOptions": {
        "type": "object",
        "properties": {
          "formats": {
            "minItems": 1,
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutputFormat"
            },
            "description": "Output formats to generate. Multiple formats can be requested (e.g. ['gltf', 'stpz']) - each produces a separate downloadable result. Do not include both 'step' and 'stpz' - only one STEP variant is supported per request."
          },
          "meshPrecision": {
            "description": "Tessellation precision for mesh-based outputs (decomposed-mesh and gltf). Lower values produce denser meshes. Only ignored when output is limited to step/stpz formats.",
            "$ref": "#/components/schemas/MeshPrecision"
          },
          "gltfMeshPrecision": {
            "description": "Override tessellation precision specifically for glTF output. When set, takes precedence over meshPrecision for glTF only.",
            "$ref": "#/components/schemas/MeshPrecision"
          },
          "adjustYtoZ": {
            "description": "Convert from Y-up to Z-up coordinate system in the output. Useful for software that expects Z-up.",
            "type": "boolean"
          }
        },
        "required": [
          "formats"
        ],
        "additionalProperties": false,
        "description": "Controls which output formats are generated and their quality settings"
      },
      "OutputFormat": {
        "type": "string",
        "enum": [
          "step",
          "stpz",
          "decomposed-mesh",
          "gltf"
        ],
        "description": "Output file format: 'step' (raw STEP), 'stpz' (gzip-compressed STEP), 'decomposed-mesh' (triangulated JSON), 'gltf' (glTF 2.0 binary .glb). Note: 'step' and 'stpz' are mutually exclusive - choose one or the other, not both."
      },
      "BatchModelSubmissionBody": {
        "type": "object",
        "properties": {
          "items": {
            "minItems": 1,
            "maxItems": 50,
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "params": {
                  "type": "object",
                  "propertyNames": {
                    "type": "string"
                  },
                  "additionalProperties": {}
                }
              }
            },
            "description": "Array of parameter variations. Each item produces a separate output with the same model."
          },
          "outputs": {
            "description": "Output format configuration applied to every item in the batch",
            "$ref": "#/components/schemas/OutputOptions"
          }
        },
        "required": [
          "items",
          "outputs"
        ],
        "description": "Submit multiple parameter variations of the same model for parallel generation"
      },
      "DragonCupBody": {
        "type": "object",
        "properties": {
          "params": {
            "$ref": "#/components/schemas/DragonCupParams"
          },
          "outputs": {
            "$ref": "#/components/schemas/OutputOptions"
          }
        },
        "required": [
          "outputs"
        ],
        "additionalProperties": false,
        "description": "Request body for the Dragon Cup model"
      },
      "DragonCupParams": {
        "type": "object",
        "properties": {
          "height": {
            "description": "Total cup height (cm)",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 50
          },
          "radiusBottom": {
            "description": "Radius at the base (cm)",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 30
          },
          "radiusTopOffset": {
            "description": "How much the top radius differs from bottom",
            "type": "number",
            "minimum": 0,
            "maximum": 20
          },
          "radiusMidOffset": {
            "description": "Mid-section radius offset",
            "type": "number",
            "minimum": 0,
            "maximum": 20
          },
          "rotationTopAngle": {
            "description": "Top twist angle (degrees)",
            "type": "number",
            "minimum": -360,
            "maximum": 360
          },
          "rotationMidAngle": {
            "description": "Mid twist angle (degrees)",
            "type": "number",
            "minimum": -360,
            "maximum": 360
          },
          "nrSkinCellsVertical": {
            "description": "Vertical skin cell count",
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 30
          },
          "nrSkinCellsHorizontal": {
            "description": "Horizontal skin cell count",
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 50
          },
          "nrSkinCellDivisionsTop": {
            "description": "Subdivisions at top of each cell",
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 10
          },
          "nrSkinCellDivisionsBottom": {
            "description": "Subdivisions at bottom of each cell",
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 10
          },
          "skinCellOuterHeight": {
            "description": "Outer cell extrusion height",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 5
          },
          "skinCellInnerHeight": {
            "description": "Inner cell extrusion height",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 5
          },
          "skinCellBottomHeight": {
            "description": "Bottom cell height",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 5
          },
          "skinCellTopHeight": {
            "description": "Top cell height",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 5
          },
          "thickness": {
            "description": "Shell wall thickness",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 5
          },
          "bottomThickness": {
            "description": "Bottom plate thickness",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 5
          },
          "precision": {
            "description": "Geometry precision (lower = finer)",
            "$ref": "#/components/schemas/MeshPrecision"
          },
          "rotation": {
            "description": "Final rotation angle (degrees)",
            "type": "number",
            "minimum": -360,
            "maximum": 360
          },
          "scale": {
            "description": "Uniform scale factor",
            "$ref": "#/components/schemas/PositiveScale"
          },
          "origin": {
            "description": "[x, y, z] placement origin",
            "$ref": "#/components/schemas/Point3"
          },
          "direction": {
            "description": "[x, y, z] up direction",
            "$ref": "#/components/schemas/Point3"
          }
        },
        "additionalProperties": false,
        "description": "Dragon Cup model parameters - all optional, sane defaults apply"
      },
      "PositiveScale": {
        "type": "number",
        "minimum": 0.000001,
        "maximum": 1000,
        "description": "Uniform scale factor. Range: [1e-6, 1000]"
      },
      "Point3": {
        "type": "array",
        "prefixItems": [
          {
            "type": "number",
            "minimum": -1000,
            "maximum": 1000
          },
          {
            "type": "number",
            "minimum": -1000,
            "maximum": 1000
          },
          {
            "type": "number",
            "minimum": -1000,
            "maximum": 1000
          }
        ],
        "description": "3D point as [x, y, z] tuple, each coordinate in [-1000, 1000]"
      },
      "PhoneNestBody": {
        "type": "object",
        "properties": {
          "params": {
            "$ref": "#/components/schemas/PhoneNestParams"
          },
          "outputs": {
            "$ref": "#/components/schemas/OutputOptions"
          }
        },
        "required": [
          "outputs"
        ],
        "additionalProperties": false,
        "description": "Request body for the Phone Nest model"
      },
      "PhoneNestParams": {
        "type": "object",
        "properties": {
          "heightBottom": {
            "description": "Height of the bottom section (cm)",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 50
          },
          "heightTop": {
            "description": "Height of the top section (cm)",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 50
          },
          "widthBack": {
            "description": "Width at the back (cm)",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 50
          },
          "widthFront": {
            "description": "Width at the front (cm)",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 50
          },
          "length": {
            "description": "Depth / length (cm)",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 50
          },
          "backOffset": {
            "description": "Back curve offset (cm)",
            "type": "number",
            "minimum": 0,
            "maximum": 30
          },
          "thickness": {
            "description": "Shell wall thickness",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 5
          },
          "applyOrnaments": {
            "description": "Add decorative perforations to the surface",
            "type": "boolean"
          },
          "filletRadius": {
            "description": "Fillet radius on loft edges",
            "type": "number",
            "minimum": 0,
            "maximum": 15
          },
          "phoneHeight": {
            "description": "Phone mock-up height (cm)",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 35
          },
          "phoneWidth": {
            "description": "Phone mock-up width (cm)",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 35
          },
          "phoneThickness": {
            "description": "Phone mock-up thickness (cm)",
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 3
          },
          "precision": {
            "description": "Geometry precision (lower = finer)",
            "$ref": "#/components/schemas/MeshPrecision"
          },
          "rotation": {
            "description": "Final rotation angle (degrees)",
            "type": "number",
            "minimum": -360,
            "maximum": 360
          },
          "scale": {
            "description": "Uniform scale factor",
            "$ref": "#/components/schemas/PositiveScale"
          },
          "origin": {
            "description": "[x, y, z] placement origin",
            "$ref": "#/components/schemas/Point3"
          },
          "direction": {
            "description": "[x, y, z] up direction",
            "$ref": "#/components/schemas/Point3"
          }
        },
        "additionalProperties": false,
        "description": "Phone Nest model parameters - all optional, sane defaults apply"
      },
      "StepToGltfBody": {
        "type": "object",
        "properties": {
          "stepFileId": {
            "type": "string",
            "minLength": 1,
            "description": "ID of the previously uploaded STEP file (returned by the file upload endpoint)"
          },
          "meshPrecision": {
            "description": "Mesh linear deflection. When meshRelative is true (default), this is a fraction of each edge's length (e.g. 0.005 = 0.5%). When false, it is an absolute value in model units (mm for STEP).",
            "$ref": "#/components/schemas/MeshPrecision"
          },
          "meshAngle": {
            "description": "Angular deflection in radians for mesh tessellation - controls curvature approximation. Smaller values produce smoother curved surfaces.",
            "$ref": "#/components/schemas/MeshAngle"
          },
          "meshRelative": {
            "description": "Use size-aware relative deflection per face. When true, meshPrecision is a fraction of each edge's length. Set to false for absolute deflection in model units.",
            "type": "boolean"
          },
          "internalVerticesMode": {
            "description": "Add interior vertices for better curved face fidelity (slower, set false for speed).",
            "type": "boolean"
          },
          "controlSurfaceDeflection": {
            "description": "Extra post-pass refining triangles that bulge beyond the deflection (slower, set false for speed).",
            "type": "boolean"
          }
        },
        "required": [
          "stepFileId"
        ],
        "additionalProperties": false,
        "description": "Convert a STEP file to glTF format with default settings. Upload the STEP file first, then pass its ID here."
      },
      "MeshAngle": {
        "type": "number",
        "minimum": 0.01,
        "maximum": 3.141592653589793,
        "description": "Mesh angular deflection in radians. Range: [0.01, π]"
      },
      "StepToGltfAdvancedBody": {
        "type": "object",
        "properties": {
          "stepFileId": {
            "type": "string",
            "minLength": 1,
            "description": "ID of the previously uploaded STEP file (returned by the file upload endpoint)"
          },
          "options": {
            "description": "Advanced conversion settings. If omitted, sensible defaults are used.",
            "$ref": "#/components/schemas/ConvertAdvancedOptions"
          }
        },
        "required": [
          "stepFileId"
        ],
        "additionalProperties": false,
        "description": "Convert a STEP file to glTF with full control over tessellation, naming, coordinate systems, and output format."
      },
      "ConvertAdvancedOptions": {
        "type": "object",
        "properties": {
          "readColors": {
            "description": "Extract color attributes from the STEP file and apply to glTF materials",
            "type": "boolean"
          },
          "readNames": {
            "description": "Extract product/instance names from the STEP file and apply to glTF node names",
            "type": "boolean"
          },
          "readMaterials": {
            "description": "Extract material definitions from the STEP file",
            "type": "boolean"
          },
          "readLayers": {
            "description": "Extract layer/group structure from the STEP file",
            "type": "boolean"
          },
          "readProps": {
            "description": "Extract custom properties (e.g. part numbers, metadata) from the STEP file",
            "type": "boolean"
          },
          "meshDeflection": {
            "description": "Mesh linear deflection. When meshRelative is true (default), this is a fraction of each edge's length (e.g. 0.005 = 0.5%) so deflection auto-scales with feature size. When false, it is absolute in model units (mm for STEP).",
            "$ref": "#/components/schemas/MeshPrecision"
          },
          "meshAngle": {
            "description": "Angular deflection for mesh tessellation (radians) - controls curvature approximation",
            "$ref": "#/components/schemas/MeshAngle"
          },
          "meshParallel": {
            "description": "Enable parallel tessellation for faster processing",
            "type": "boolean"
          },
          "meshRelative": {
            "description": "Use size-aware relative deflection per face. When true, meshDeflection is a fraction of each edge's length. Set to false for absolute deflection in model units.",
            "type": "boolean"
          },
          "internalVerticesMode": {
            "description": "Add interior vertices for better curved face fidelity (slower, set false for speed).",
            "type": "boolean"
          },
          "controlSurfaceDeflection": {
            "description": "Extra post-pass refining triangles that bulge beyond the deflection (slower, set false for speed).",
            "type": "boolean"
          },
          "faceCountThreshold": {
            "description": "Face count threshold for per-sub-shape meshing fallback. Default -1 means single-pass meshing of the whole compound (fastest). Set to a positive value (e.g. 100000) to fall back to per-solid meshing for very large assemblies in memory-constrained environments.",
            "type": "integer",
            "minimum": -1,
            "maximum": 500000
          },
          "mergeFaces": {
            "description": "Merge co-planar adjacent faces to reduce mesh complexity",
            "type": "boolean"
          },
          "splitIndices16": {
            "description": "Split meshes so each uses 16-bit index buffers (max 65535 vertices per mesh). Improves compatibility with some renderers.",
            "type": "boolean"
          },
          "parallelWrite": {
            "description": "Write glTF output in parallel for faster file generation",
            "type": "boolean"
          },
          "embedTextures": {
            "description": "Embed texture data directly in the .glb file instead of referencing external URIs",
            "type": "boolean"
          },
          "forceUVExport": {
            "description": "Generate UV coordinates even when no textures are present (useful for post-processing)",
            "type": "boolean"
          },
          "nodeNameFormat": {
            "description": "Strategy for naming glTF nodes from STEP product/instance labels",
            "$ref": "#/components/schemas/GltfNameFormat"
          },
          "meshNameFormat": {
            "description": "Strategy for naming glTF meshes from STEP product/instance labels",
            "$ref": "#/components/schemas/GltfNameFormat"
          },
          "transformFormat": {
            "description": "Transform representation in glTF: 'compact' (default), 'mat4' (4x4 matrix), or 'trs' (translation/rotation/scale)",
            "$ref": "#/components/schemas/GltfTransformFormat"
          },
          "adjustZtoY": {
            "description": "Convert from Z-up (CAD convention) to Y-up (glTF/WebGL convention) coordinate system",
            "type": "boolean"
          },
          "scale": {
            "description": "Uniform scale factor applied to the entire model (e.g. 0.001 to convert mm to meters)",
            "$ref": "#/components/schemas/PositiveScale"
          }
        },
        "additionalProperties": false,
        "description": "Fine-grained options controlling STEP → glTF conversion: tessellation quality, attribute extraction, coordinate systems, and output format."
      },
      "GltfNameFormat": {
        "type": "string",
        "enum": [
          "empty",
          "product",
          "instance",
          "instanceOrProduct",
          "productOrInstance",
          "productAndInstance",
          "productAndInstanceAndOcaf"
        ],
        "description": "How node/mesh names are derived from STEP product/instance labels"
      },
      "GltfTransformFormat": {
        "type": "string",
        "enum": [
          "compact",
          "mat4",
          "trs"
        ],
        "description": "Transform representation in glTF output"
      },
      "StepToGltfWithDracoBody": {
        "type": "object",
        "properties": {
          "stepFileId": {
            "type": "string",
            "minLength": 1,
            "description": "ID of the previously uploaded STEP file (returned by the file upload endpoint)"
          },
          "meshPrecision": {
            "description": "Mesh linear deflection. When meshRelative is true (default), this is a fraction of each edge's length (e.g. 0.005 = 0.5%). When false, it is an absolute value in model units (mm for STEP).",
            "$ref": "#/components/schemas/MeshPrecision"
          },
          "meshAngle": {
            "description": "Angular deflection in radians for mesh tessellation - controls curvature approximation. Smaller values produce smoother curved surfaces.",
            "$ref": "#/components/schemas/MeshAngle"
          },
          "meshRelative": {
            "description": "Use size-aware relative deflection per face. When true, meshPrecision is a fraction of each edge's length. Set to false for absolute deflection in model units.",
            "type": "boolean"
          },
          "internalVerticesMode": {
            "description": "Add interior vertices for better curved face fidelity (slower, set false for speed).",
            "type": "boolean"
          },
          "controlSurfaceDeflection": {
            "description": "Extra post-pass refining triangles that bulge beyond the deflection (slower, set false for speed).",
            "type": "boolean"
          },
          "draco": {
            "description": "Draco compression options. If omitted, sensible defaults are used (level 7, 14/10/12/8/12 bits).",
            "$ref": "#/components/schemas/DracoCompressionOptions"
          }
        },
        "required": [
          "stepFileId"
        ],
        "additionalProperties": false,
        "description": "Convert a STEP file to Draco-compressed glTF with default settings. Upload the STEP file first, then pass its ID here."
      },
      "DracoCompressionOptions": {
        "type": "object",
        "properties": {
          "useDraco": {
            "description": "Apply Draco geometry compression to the glTF output. Defaults to true for the *-with-draco endpoints.",
            "type": "boolean"
          },
          "dracoCompressionLevel": {
            "description": "Draco compression level (0 = fastest/largest, 10 = slowest/smallest). Typical good default is 7.",
            "type": "integer",
            "minimum": 0,
            "maximum": 10
          },
          "dracoQuantizePositionBits": {
            "description": "Quantization bits for vertex positions (higher = more precise, larger file). Typical: 14.",
            "type": "integer",
            "minimum": 0,
            "maximum": 31
          },
          "dracoQuantizeNormalBits": {
            "description": "Quantization bits for normal vectors. Typical: 10.",
            "type": "integer",
            "minimum": 0,
            "maximum": 31
          },
          "dracoQuantizeTexcoordBits": {
            "description": "Quantization bits for texture coordinates. Typical: 12.",
            "type": "integer",
            "minimum": 0,
            "maximum": 31
          },
          "dracoQuantizeColorBits": {
            "description": "Quantization bits for vertex colors. Typical: 8.",
            "type": "integer",
            "minimum": 0,
            "maximum": 31
          },
          "dracoQuantizeGenericBits": {
            "description": "Quantization bits for generic vertex attributes. Typical: 12.",
            "type": "integer",
            "minimum": 0,
            "maximum": 31
          },
          "dracoUnifiedQuantization": {
            "description": "Use unified quantization across all meshes (better when meshes share a coordinate frame).",
            "type": "boolean"
          }
        },
        "additionalProperties": false,
        "description": "Draco geometry compression options applied during glTF export."
      },
      "StepToGltfAdvancedWithDracoBody": {
        "type": "object",
        "properties": {
          "stepFileId": {
            "type": "string",
            "minLength": 1,
            "description": "ID of the previously uploaded STEP file (returned by the file upload endpoint)"
          },
          "options": {
            "description": "Advanced conversion settings. If omitted, sensible defaults are used.",
            "$ref": "#/components/schemas/ConvertAdvancedOptions"
          },
          "draco": {
            "description": "Draco compression options. If omitted, sensible defaults are used (level 7, 14/10/12/8/12 bits).",
            "$ref": "#/components/schemas/DracoCompressionOptions"
          }
        },
        "required": [
          "stepFileId"
        ],
        "additionalProperties": false,
        "description": "Convert a STEP file to Draco-compressed glTF with full control over tessellation, naming, coordinate systems, output format, and Draco quantization."
      },
      "UnfoldFaceToFlatBody": {
        "type": "object",
        "properties": {
          "stepFileId": {
            "type": "string",
            "minLength": 1,
            "description": "ID of the previously uploaded STEP file (returned by the file upload endpoint). Both plain STEP (.step/.stp) and gzip-compressed STEP (.stpz) uploads are accepted - compression is detected automatically."
          },
          "faceIndex": {
            "default": 0,
            "description": "0-based index of the developable face (in the shape's BRepGraph face array) to unfold.",
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          },
          "outputs": {
            "description": "Output formats for the flattened face geometry. Defaults to glTF.",
            "$ref": "#/components/schemas/OutputOptions"
          }
        },
        "required": [
          "stepFileId"
        ],
        "additionalProperties": false,
        "description": "Unfold a single developable face of an uploaded STEP shape to a flat face."
      },
      "UnfoldFaceReportBody": {
        "type": "object",
        "properties": {
          "stepFileId": {
            "type": "string",
            "minLength": 1,
            "description": "ID of the previously uploaded STEP file (returned by the file upload endpoint). Both plain STEP (.step/.stp) and gzip-compressed STEP (.stpz) uploads are accepted - compression is detected automatically."
          },
          "faceIndex": {
            "default": 0,
            "description": "0-based index of the face to analyze.",
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "stepFileId"
        ],
        "additionalProperties": false,
        "description": "Compute the sheet-metal unfold metrics report for a single face of an uploaded STEP shape."
      },
      "UnfoldSolidToFlatBody": {
        "type": "object",
        "properties": {
          "stepFileId": {
            "type": "string",
            "minLength": 1,
            "description": "ID of the previously uploaded STEP file (returned by the file upload endpoint). Both plain STEP (.step/.stp) and gzip-compressed STEP (.stpz) uploads are accepted - compression is detected automatically."
          },
          "kFactor": {
            "default": 0.5,
            "description": "Sheet-metal K-factor (neutral-axis position), typically ~0.5.",
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "thicknessOverride": {
            "default": 0,
            "description": "Sheet thickness override in model units; 0 auto-detects thickness from face pairs.",
            "type": "number",
            "minimum": 0
          },
          "outputs": {
            "description": "Output formats for the flattened patterns. Every solid's flat is combined into one compound output per format. Defaults to glTF.",
            "$ref": "#/components/schemas/OutputOptions"
          }
        },
        "required": [
          "stepFileId"
        ],
        "additionalProperties": false,
        "description": "Flatten every constant-thickness sheet-metal solid in an uploaded STEP file to flat patterns."
      },
      "UnfoldSolidReportBody": {
        "type": "object",
        "properties": {
          "stepFileId": {
            "type": "string",
            "minLength": 1,
            "description": "ID of the previously uploaded STEP file (returned by the file upload endpoint). Both plain STEP (.step/.stp) and gzip-compressed STEP (.stpz) uploads are accepted - compression is detected automatically."
          },
          "kFactor": {
            "default": 0.5,
            "description": "Sheet-metal K-factor (neutral-axis position), typically ~0.5.",
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "thicknessOverride": {
            "default": 0,
            "description": "Sheet thickness override in model units; 0 auto-detects thickness from face pairs.",
            "type": "number",
            "minimum": 0
          }
        },
        "required": [
          "stepFileId"
        ],
        "additionalProperties": false,
        "description": "Compute per-solid sheet-metal unfold metrics for every solid in an uploaded STEP file."
      },
      "UnfoldSolidBody": {
        "type": "object",
        "properties": {
          "stepFileId": {
            "type": "string",
            "minLength": 1,
            "description": "ID of the previously uploaded STEP file (returned by the file upload endpoint). Both plain STEP (.step/.stp) and gzip-compressed STEP (.stpz) uploads are accepted - compression is detected automatically."
          },
          "kFactor": {
            "default": 0.5,
            "description": "Sheet-metal K-factor (neutral-axis position), typically ~0.5.",
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "thicknessOverride": {
            "default": 0,
            "description": "Sheet thickness override in model units; 0 auto-detects thickness from face pairs.",
            "type": "number",
            "minimum": 0
          },
          "outputs": {
            "description": "Output formats for the flattened patterns. For 'gltf'/'step'/'stpz', every solid's flat is combined into one compound output. The 'decomposed-mesh' format instead returns one mesh per solid (each tagged with its 'solidIndex'). Defaults to glTF.",
            "$ref": "#/components/schemas/OutputOptions"
          }
        },
        "required": [
          "stepFileId"
        ],
        "additionalProperties": false,
        "description": "Flatten every constant-thickness sheet-metal solid in an uploaded STEP file AND return the per-solid metrics report - both in one call."
      },
      "FileUploadBody": {
        "type": "object",
        "properties": {
          "filename": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Original filename including extension (e.g. 'part.step', 'model.obj'). Used for display and format detection."
          },
          "contentType": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "MIME type of the file (e.g. 'application/step', 'model/gltf-binary'). Must match the actual file content."
          },
          "bytes": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 1073741824,
            "description": "Exact file size in bytes. Must match the Content-Length of the subsequent PUT to the upload URL. Maximum: 1 GB."
          },
          "sha256": {
            "description": "SHA-256 hex digest of the file content. When provided, enables server-side deduplication - if an identical file was previously uploaded under this API key, the existing file is reused and no upload is needed.",
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          }
        },
        "required": [
          "filename",
          "contentType",
          "bytes"
        ],
        "additionalProperties": false,
        "description": "Request a pre-signed upload URL. After receiving the URL, PUT the raw file bytes to it within the expiration window."
      },
      "UnfoldReportError": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "ok",
          "error"
        ],
        "description": "Returned when a shape/face cannot be processed by the unfold."
      },
      "UnfoldSolidReportItem": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "ok": {
                "type": "boolean",
                "const": true
              },
              "thickness": {
                "type": "number"
              },
              "kFactor": {
                "type": "number"
              },
              "warning": {
                "type": "string"
              },
              "bends": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/UnfoldBend"
                }
              },
              "flatBBox": {
                "type": "array",
                "prefixItems": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  }
                ]
              },
              "flatArea": {
                "type": "number"
              },
              "referenceArea": {
                "type": "number"
              },
              "totalArea": {
                "type": "number"
              },
              "faces": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/UnfoldFaceEntry"
                }
              },
              "placed": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/UnfoldPlaced"
                }
              }
            },
            "required": [
              "ok",
              "thickness",
              "kFactor",
              "warning",
              "bends",
              "flatBBox",
              "flatArea",
              "referenceArea",
              "totalArea",
              "faces",
              "placed"
            ]
          },
          {
            "$ref": "#/components/schemas/UnfoldSolidReportError"
          }
        ],
        "description": "Sheet-metal unfold metrics for ONE solid (bend table + face roles + original->flat map). On failure, the ok:false branch still carries the partial diagnostics gathered before the failure."
      },
      "UnfoldBend": {
        "type": "object",
        "properties": {
          "faceIndex": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "flatA": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "flatB": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "lineStart": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "lineEnd": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "lineMid": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "bendLineLength": {
            "type": "number"
          },
          "angleDeg": {
            "type": "number"
          },
          "innerRadius": {
            "type": "number"
          },
          "outerRadius": {
            "type": "number"
          },
          "neutralRadius": {
            "type": "number"
          },
          "innerRadiusStart": {
            "type": "number"
          },
          "innerRadiusEnd": {
            "type": "number"
          },
          "outerRadiusStart": {
            "type": "number"
          },
          "outerRadiusEnd": {
            "type": "number"
          },
          "T": {
            "type": "number"
          },
          "K": {
            "type": "number"
          },
          "allowance": {
            "type": "number"
          },
          "bendDeduction": {
            "type": "number"
          },
          "outsideSetback": {
            "type": "number"
          },
          "innerArcLength": {
            "type": "number"
          },
          "outerArcLength": {
            "type": "number"
          },
          "direction": {
            "type": "string",
            "enum": [
              "up",
              "down"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "cyl",
              "cone"
            ]
          },
          "layoutApprox": {
            "type": "boolean"
          }
        },
        "required": [
          "faceIndex",
          "flatA",
          "flatB",
          "lineStart",
          "lineEnd",
          "lineMid",
          "bendLineLength",
          "angleDeg",
          "innerRadius",
          "outerRadius",
          "neutralRadius",
          "innerRadiusStart",
          "innerRadiusEnd",
          "outerRadiusStart",
          "outerRadiusEnd",
          "T",
          "K",
          "allowance",
          "bendDeduction",
          "outsideSetback",
          "innerArcLength",
          "outerArcLength",
          "direction",
          "kind",
          "layoutApprox"
        ],
        "description": "One bend: line, angle, radii (inner/outer/neutral and per-end for cones), and developed lengths."
      },
      "UnfoldFaceEntry": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "uid": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "surfaceType": {
            "type": "string",
            "enum": [
              "Plane",
              "Cylinder",
              "Cone",
              "Sphere",
              "Torus",
              "Other"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "referenceFlat",
              "otherSideFlat",
              "rim",
              "flat",
              "bend",
              "coneBend",
              "cutout",
              "ignored"
            ]
          },
          "reference": {
            "type": "boolean"
          },
          "opposite": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "area": {
            "type": "number"
          },
          "centroid": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "labelPoint": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "tangentDot": {
            "description": "Bend candidates (cylinder/cone) only: best |flange normal · cylinder radial| over the flanges. ~1 = tangent fold, ~0 = cutout/hole wall.",
            "type": "number"
          },
          "bendOutcome": {
            "description": "How the fold filter classified a bend candidate: 'fold' (kept), 'cutout' (rejected - not tangent / <2 flanges), 'duplicate' (dropped - one fold per flat pair), or '' (a bend face not on the reference side, never evaluated by the filter).",
            "type": "string",
            "enum": [
              "fold",
              "cutout",
              "duplicate",
              ""
            ]
          }
        },
        "required": [
          "index",
          "uid",
          "surfaceType",
          "role",
          "reference",
          "opposite",
          "area",
          "centroid",
          "labelPoint"
        ],
        "description": "A classified source face: stable key (index + BRepGraph uid), role, and label anchors."
      },
      "UnfoldPlaced": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "flat",
              "bendStrip"
            ]
          },
          "sourceFaceIndex": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "role": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "referenceFlat",
                  "otherSideFlat",
                  "rim",
                  "flat",
                  "bend",
                  "coneBend",
                  "cutout",
                  "ignored"
                ]
              },
              {
                "type": "string",
                "const": "unknown"
              }
            ]
          },
          "flatCentroid": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          }
        },
        "required": [
          "kind",
          "sourceFaceIndex",
          "role",
          "flatCentroid"
        ],
        "description": "A sub-shape laid into the flat compound, mapped back to its source face (compound order)."
      },
      "UnfoldSolidReportError": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "string"
          },
          "thickness": {
            "type": "number"
          },
          "kFactor": {
            "type": "number"
          },
          "warning": {
            "type": "string"
          },
          "bends": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UnfoldBend"
            }
          },
          "flatBBox": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "flatArea": {
            "type": "number"
          },
          "referenceArea": {
            "type": "number"
          },
          "totalArea": {
            "type": "number"
          },
          "faces": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UnfoldFaceEntry"
            }
          },
          "placed": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UnfoldPlaced"
            }
          }
        },
        "required": [
          "ok",
          "error"
        ],
        "description": "Returned when a solid cannot be fully unfolded; carries the partial diagnostics gathered before the failure (classified faces with roles, detected thickness, warnings) so the reason can be inspected."
      },
      "HealthResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/HealthStatus"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope for health check"
      },
      "HealthStatus": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "degraded"
            ],
            "description": "Overall health status"
          },
          "timestamp": {
            "type": "string",
            "description": "ISO 8601 timestamp"
          }
        },
        "required": [
          "status",
          "timestamp"
        ],
        "additionalProperties": false,
        "description": "API health check result indicating overall system status"
      },
      "TaskAcceptedResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/TaskCreatedResult"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "HTTP 202 response when a task is accepted for async processing"
      },
      "TaskCreatedResult": {
        "type": "object",
        "properties": {
          "taskId": {
            "type": "string",
            "description": "Unique task identifier - use this to poll for status and retrieve results",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "const": "queued",
            "description": "Initial task status (always 'queued' upon creation)"
          },
          "statusUrl": {
            "type": "string",
            "description": "Relative URL to poll for task status updates",
            "example": "/api/v1/tasks/{taskId}"
          }
        },
        "required": [
          "taskId",
          "status",
          "statusUrl"
        ],
        "additionalProperties": false,
        "description": "Returned when a CAD task is accepted. Poll the statusUrl to track progress."
      },
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "$ref": "#/components/schemas/ErrorDetail"
          }
        },
        "required": [
          "ok",
          "error"
        ],
        "additionalProperties": false,
        "description": "Standard error envelope"
      },
      "ErrorDetail": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Machine-readable error code",
            "example": "VALIDATION_ERROR"
          },
          "message": {
            "type": "string",
            "description": "Human-readable error message"
          },
          "details": {
            "description": "Optional structured error context"
          },
          "requestId": {
            "description": "Request ID for tracing",
            "type": "string"
          }
        },
        "required": [
          "code",
          "message"
        ],
        "additionalProperties": false,
        "description": "Structured error information with machine-readable code and optional debugging context"
      },
      "CompoundTaskAcceptedResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/CompoundTaskCreatedResult"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "HTTP 202 response when a compound (parallel) task is accepted"
      },
      "CompoundTaskCreatedResult": {
        "type": "object",
        "properties": {
          "taskId": {
            "type": "string",
            "description": "Parent compound task ID for tracking overall progress"
          },
          "kind": {
            "type": "string",
            "const": "compound"
          },
          "subTaskCount": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991,
            "description": "Total number of sub-tasks created (one per item in the request)"
          },
          "statusUrl": {
            "type": "string",
            "description": "Relative URL to poll the parent task status",
            "example": "/api/v1/tasks/{taskId}"
          },
          "subTasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SubTaskSummary"
            }
          }
        },
        "required": [
          "taskId",
          "kind",
          "subTaskCount",
          "statusUrl",
          "subTasks"
        ],
        "additionalProperties": false,
        "description": "Returned when a compound (parallel) task is accepted. Contains initial status for all sub-tasks."
      },
      "SubTaskSummary": {
        "type": "object",
        "properties": {
          "taskId": {
            "type": "string",
            "description": "Unique identifier for this sub-task (can be polled individually)"
          },
          "index": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Zero-based position of this sub-task within the compound"
          },
          "status": {
            "$ref": "#/components/schemas/TaskStatus"
          },
          "progress": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Completion percentage (0–100) or null if not yet started"
          },
          "computeMs": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Actual compute time in milliseconds (null until completed)"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Error message if sub-task failed, null otherwise"
          },
          "downloadFormats": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Available output format keys (e.g. ['glb', 'stpz']) for downloading results"
          }
        },
        "required": [
          "taskId",
          "index",
          "status",
          "progress",
          "computeMs",
          "error",
          "downloadFormats"
        ],
        "additionalProperties": false,
        "description": "Status summary for one sub-task within a compound (parallel) task"
      },
      "TaskStatus": {
        "type": "string",
        "enum": [
          "waiting",
          "queued",
          "processing",
          "completed",
          "failed",
          "cancelled",
          "expired"
        ],
        "description": "Task lifecycle state: waiting (pending dependencies), queued (in queue), processing (actively computing), completed (result available), failed (error occurred), cancelled (user-cancelled), expired (result TTL exceeded)"
      },
      "ModelListResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/ModelList"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing the list of available models"
      },
      "ModelList": {
        "type": "object",
        "properties": {
          "models": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Available model slugs that can be used with the /models/{name} endpoints"
          }
        },
        "required": [
          "models"
        ],
        "additionalProperties": false,
        "description": "List of all available parametric models"
      },
      "ModelBatchDefinitionsResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/ModelBatchDefinitions"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing multiple model definitions"
      },
      "ModelBatchDefinitions": {
        "type": "object",
        "properties": {
          "definitions": {
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "$ref": "#/components/schemas/ModelDefinition"
            },
            "description": "Model definitions keyed by slug. Each contains parameter schemas and defaults."
          }
        },
        "required": [
          "definitions"
        ],
        "additionalProperties": false,
        "description": "Definitions for multiple models in a single response"
      },
      "ModelDefinition": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Model slug used in API paths (e.g. 'dragon-cup')"
          },
          "displayName": {
            "type": "string",
            "description": "Human-friendly model name for UI display"
          },
          "description": {
            "type": "string",
            "description": "What this model creates and its key characteristics"
          },
          "params": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ModelParamDefinition"
            },
            "description": "All accepted parameters with types, descriptions, and defaults"
          },
          "defaults": {
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {},
            "description": "Complete default parameter values map"
          }
        },
        "required": [
          "name",
          "displayName",
          "description",
          "params",
          "defaults"
        ],
        "additionalProperties": false,
        "description": "Full definition of a parametric model including all parameters and their constraints"
      },
      "ModelParamDefinition": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Parameter name as used in the request body"
          },
          "type": {
            "description": "Expected data type for this parameter",
            "$ref": "#/components/schemas/ModelParamApiType"
          },
          "description": {
            "type": "string",
            "description": "Human-readable explanation of what this parameter controls"
          },
          "default": {
            "description": "Default value used when the parameter is omitted"
          },
          "items": {
            "description": "Element type descriptor (only present for array parameters)",
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              }
            },
            "required": [
              "type"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "name",
          "type",
          "description",
          "default"
        ],
        "additionalProperties": false,
        "description": "Schema definition for a single model parameter"
      },
      "ModelParamApiType": {
        "type": "string",
        "enum": [
          "number",
          "integer",
          "boolean",
          "array"
        ],
        "description": "Data type of a model parameter: 'number' (float), 'integer' (whole number), 'boolean' (true/false), 'array' (list of values)"
      },
      "ModelDefinitionResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/ModelDefinition"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing a single model's definition"
      },
      "UploadResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/UploadResult"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing the pre-signed upload URL and file metadata"
      },
      "UploadResult": {
        "type": "object",
        "properties": {
          "fileId": {
            "type": "string",
            "description": "Assigned file identifier - use this to reference the file in subsequent API calls",
            "format": "uuid"
          },
          "uploadUrl": {
            "type": "string",
            "description": "Pre-signed URL to PUT the raw file bytes to. Send the file content with Content-Type and Content-Length headers matching the request.",
            "format": "uri"
          },
          "expiresIn": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991,
            "description": "Seconds until the upload URL expires. You must complete the PUT within this window."
          },
          "maxBytes": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 9007199254740991,
            "description": "Maximum allowed file size in bytes for this upload. Determined by your plan tier."
          },
          "status": {
            "type": "string",
            "const": "pending"
          }
        },
        "required": [
          "fileId",
          "uploadUrl",
          "expiresIn",
          "maxBytes",
          "status"
        ],
        "additionalProperties": false,
        "description": "Response after requesting a file upload. Contains the pre-signed URL to PUT your file to."
      },
      "FileListResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/FileList"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing a paginated file list"
      },
      "FileList": {
        "type": "object",
        "properties": {
          "files": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FileDetail"
            }
          },
          "page": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 1000
          },
          "limit": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 50
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "files",
          "page",
          "limit",
          "total"
        ],
        "additionalProperties": false,
        "description": "Paginated list of uploaded files with total count"
      },
      "FileDetail": {
        "type": "object",
        "properties": {
          "fileId": {
            "type": "string",
            "description": "Unique file identifier used to reference this file in CAD operations",
            "format": "uuid"
          },
          "filename": {
            "type": "string",
            "description": "Original filename as provided during upload request"
          },
          "contentType": {
            "type": "string",
            "description": "MIME type of the file content"
          },
          "bytes": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0,
                "maximum": 9007199254740991
              },
              {
                "type": "null"
              }
            ],
            "description": "File size in bytes. Null while status is 'pending' (not yet uploaded)."
          },
          "sha256": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "SHA-256 hex digest of the file content. Null if not provided during upload. Used for deduplication."
          },
          "status": {
            "$ref": "#/components/schemas/FileStatus"
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601 timestamp when the upload was initiated"
          },
          "confirmedAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601 timestamp when the upload was confirmed. Null if not yet confirmed."
          },
          "downloadUrl": {
            "description": "Pre-signed download URL. Only present for confirmed files. Expires after 1 hour.",
            "type": "string"
          }
        },
        "required": [
          "fileId",
          "filename",
          "contentType",
          "bytes",
          "sha256",
          "status",
          "createdAt",
          "confirmedAt"
        ],
        "additionalProperties": false,
        "description": "Full metadata for an uploaded file"
      },
      "FileStatus": {
        "type": "string",
        "enum": [
          "pending",
          "confirmed",
          "expired"
        ],
        "description": "File lifecycle state: 'pending' (upload URL issued, awaiting PUT), 'confirmed' (upload verified and file is usable), 'expired' (upload URL expired before completion)"
      },
      "FileDetailResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/FileDetail"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing a single file's metadata"
      },
      "DeleteFileResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/DeleteResult"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success response for file deletion"
      },
      "DeleteResult": {
        "type": "object",
        "properties": {
          "deleted": {
            "type": "boolean",
            "const": true
          }
        },
        "required": [
          "deleted"
        ],
        "additionalProperties": false,
        "description": "Confirmation that a resource was successfully deleted"
      },
      "ConfirmResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/ConfirmResult"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope confirming a file upload was verified"
      },
      "ConfirmResult": {
        "type": "object",
        "properties": {
          "fileId": {
            "type": "string",
            "description": "File identifier that was confirmed",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "const": "confirmed"
          },
          "bytes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Actual file size in bytes as stored on the server"
          },
          "contentType": {
            "type": "string",
            "description": "MIME type of the confirmed file"
          }
        },
        "required": [
          "fileId",
          "status",
          "bytes",
          "contentType"
        ],
        "additionalProperties": false,
        "description": "Returned after a file upload is verified. The file is now ready to be used in CAD operations."
      },
      "TaskListResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/TaskList"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing a paginated task list"
      },
      "TaskList": {
        "type": "object",
        "properties": {
          "tasks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TaskDetail"
            }
          },
          "page": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 1000
          },
          "limit": {
            "type": "integer",
            "exclusiveMinimum": 0,
            "maximum": 50
          },
          "total": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991
          }
        },
        "required": [
          "tasks",
          "page",
          "limit",
          "total"
        ],
        "additionalProperties": false,
        "description": "Paginated list of tasks with total count"
      },
      "TaskDetail": {
        "type": "object",
        "properties": {
          "taskId": {
            "type": "string",
            "description": "Unique task identifier for polling and result retrieval",
            "format": "uuid"
          },
          "kind": {
            "$ref": "#/components/schemas/TaskKind"
          },
          "label": {
            "type": "string",
            "description": "Human-readable task label",
            "example": "Model: dragon-cup"
          },
          "status": {
            "$ref": "#/components/schemas/TaskStatus"
          },
          "progress": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "description": "Completion percentage (0–100) or null if progress is not tracked for this task kind"
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "Error message when status is 'failed'. Null for all other statuses."
          },
          "resultParts": {
            "anyOf": [
              {
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {
                  "$ref": "#/components/schemas/ResultPartMeta"
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "Available result files keyed by format (e.g. 'glb', 'step'). Null until task completes."
          },
          "metadata": {
            "anyOf": [
              {
                "type": "object",
                "propertyNames": {
                  "type": "string"
                },
                "additionalProperties": {}
              },
              {
                "type": "null"
              }
            ],
            "description": "Additional task metadata (e.g. resolved parameters, model name). Shape varies by task kind."
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601 timestamp when the task was created"
          },
          "startedAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601 timestamp when processing began. Null if still queued."
          },
          "finishedAt": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "description": "ISO 8601 timestamp when the task completed (success or failure). Null if still running."
          },
          "expiresAt": {
            "type": "string",
            "description": "ISO 8601 timestamp when task results will be automatically deleted"
          }
        },
        "required": [
          "taskId",
          "kind",
          "label",
          "status",
          "progress",
          "error",
          "resultParts",
          "metadata",
          "createdAt",
          "startedAt",
          "finishedAt",
          "expiresAt"
        ],
        "additionalProperties": false,
        "description": "Complete task status including lifecycle timestamps, progress, and result metadata"
      },
      "TaskKind": {
        "type": "string",
        "enum": [
          "cad",
          "model",
          "convert-simple",
          "convert-advanced",
          "pipeline",
          "compound",
          "tenant"
        ],
        "description": "Type of work: 'cad' (single operation), 'model' (parametric model), 'convert-simple'/'convert-advanced' (file conversion), 'pipeline' (sequential chain), 'compound' (parallel batch), 'tenant' (tenant-scoped)"
      },
      "ResultPartMeta": {
        "type": "object",
        "properties": {
          "mime": {
            "type": "string",
            "description": "MIME type of the result file (e.g. 'model/gltf-binary', 'application/step')"
          },
          "bytes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Size of the result file in bytes"
          }
        },
        "required": [
          "mime",
          "bytes"
        ],
        "additionalProperties": false,
        "description": "Metadata about a single downloadable result part"
      },
      "TaskDetailResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/TaskDetail"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing a single task's full details"
      },
      "CancelTaskResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/CancelResult"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success response for task cancellation"
      },
      "CancelResult": {
        "type": "object",
        "properties": {
          "cancelled": {
            "type": "boolean",
            "const": true
          },
          "previousStatus": {
            "type": "string",
            "description": "Status before cancellation"
          }
        },
        "required": [
          "cancelled",
          "previousStatus"
        ],
        "additionalProperties": false,
        "description": "Confirmation that a task was successfully cancelled"
      },
      "TaskOrCompoundResultResponse": {
        "anyOf": [
          {
            "$ref": "#/components/schemas/TaskResultResponse"
          },
          {
            "$ref": "#/components/schemas/CompoundResultManifestResponse"
          }
        ],
        "description": "Result download link for standard tasks, or a compound manifest with per-sub-task download links"
      },
      "TaskResultResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/TaskResultDownload"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing a download link for a task result"
      },
      "TaskResultDownload": {
        "type": "object",
        "properties": {
          "downloadUrl": {
            "type": "string",
            "description": "Pre-signed URL to download the result file. Expires after 1 hour.",
            "format": "uri"
          },
          "filename": {
            "type": "string",
            "description": "Suggested filename for the download",
            "example": "result.glb"
          }
        },
        "required": [
          "downloadUrl",
          "filename"
        ],
        "additionalProperties": false,
        "description": "Download information for a completed task result"
      },
      "CompoundResultManifestResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/CompoundResultManifest"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing the compound task result manifest with per-sub-task download links"
      },
      "CompoundResultManifest": {
        "type": "object",
        "properties": {
          "manifest": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompoundResultEntry"
            },
            "description": "Per-sub-task status and download links"
          }
        },
        "required": [
          "manifest"
        ],
        "additionalProperties": false,
        "description": "Result manifest for a completed compound task"
      },
      "CompoundResultEntry": {
        "type": "object",
        "properties": {
          "taskId": {
            "type": "string",
            "description": "Sub-task ID"
          },
          "index": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Position in the compound task"
          },
          "status": {
            "$ref": "#/components/schemas/TaskStatus"
          },
          "downloadUrls": {
            "description": "Download URLs keyed by format (only if completed)",
            "type": "object",
            "propertyNames": {
              "type": "string"
            },
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "required": [
          "taskId",
          "index",
          "status"
        ],
        "additionalProperties": false,
        "description": "Status/result info for one sub-task in a compound manifest"
      },
      "TaskResultsResponse": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": true
          },
          "data": {
            "$ref": "#/components/schemas/TaskResultsDownload"
          }
        },
        "required": [
          "ok",
          "data"
        ],
        "additionalProperties": false,
        "description": "Success envelope containing download links for all result formats"
      },
      "TaskResultsDownload": {
        "type": "object",
        "properties": {
          "downloads": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "format": {
                  "type": "string",
                  "description": "Result format key (e.g. 'glb', 'step', 'metadata')"
                },
                "downloadUrl": {
                  "type": "string",
                  "description": "Pre-signed URL to download the result file. Expires after 1 hour.",
                  "format": "uri"
                },
                "filename": {
                  "type": "string",
                  "description": "Suggested filename for the download",
                  "example": "result.glb"
                }
              },
              "required": [
                "format",
                "downloadUrl",
                "filename"
              ],
              "additionalProperties": false
            },
            "description": "Download links for all available result formats"
          }
        },
        "required": [
          "downloads"
        ],
        "additionalProperties": false,
        "description": "Download information for all available result formats of a completed task"
      },
      "UnfoldReportErrorOutput": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "ok",
          "error"
        ],
        "additionalProperties": false,
        "description": "Returned when a shape/face cannot be processed by the unfold."
      },
      "UnfoldSolidReportItemOutput": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "ok": {
                "type": "boolean",
                "const": true
              },
              "thickness": {
                "type": "number"
              },
              "kFactor": {
                "type": "number"
              },
              "warning": {
                "type": "string"
              },
              "bends": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/UnfoldBendOutput"
                }
              },
              "flatBBox": {
                "type": "array",
                "prefixItems": [
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  },
                  {
                    "type": "number"
                  }
                ]
              },
              "flatArea": {
                "type": "number"
              },
              "referenceArea": {
                "type": "number"
              },
              "totalArea": {
                "type": "number"
              },
              "faces": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/UnfoldFaceEntryOutput"
                }
              },
              "placed": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/UnfoldPlacedOutput"
                }
              }
            },
            "required": [
              "ok",
              "thickness",
              "kFactor",
              "warning",
              "bends",
              "flatBBox",
              "flatArea",
              "referenceArea",
              "totalArea",
              "faces",
              "placed"
            ],
            "additionalProperties": false
          },
          {
            "$ref": "#/components/schemas/UnfoldSolidReportErrorOutput"
          }
        ],
        "description": "Sheet-metal unfold metrics for ONE solid (bend table + face roles + original->flat map). On failure, the ok:false branch still carries the partial diagnostics gathered before the failure."
      },
      "UnfoldBendOutput": {
        "type": "object",
        "properties": {
          "faceIndex": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "flatA": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "flatB": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "lineStart": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "lineEnd": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "lineMid": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "bendLineLength": {
            "type": "number"
          },
          "angleDeg": {
            "type": "number"
          },
          "innerRadius": {
            "type": "number"
          },
          "outerRadius": {
            "type": "number"
          },
          "neutralRadius": {
            "type": "number"
          },
          "innerRadiusStart": {
            "type": "number"
          },
          "innerRadiusEnd": {
            "type": "number"
          },
          "outerRadiusStart": {
            "type": "number"
          },
          "outerRadiusEnd": {
            "type": "number"
          },
          "T": {
            "type": "number"
          },
          "K": {
            "type": "number"
          },
          "allowance": {
            "type": "number"
          },
          "bendDeduction": {
            "type": "number"
          },
          "outsideSetback": {
            "type": "number"
          },
          "innerArcLength": {
            "type": "number"
          },
          "outerArcLength": {
            "type": "number"
          },
          "direction": {
            "type": "string",
            "enum": [
              "up",
              "down"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "cyl",
              "cone"
            ]
          },
          "layoutApprox": {
            "type": "boolean"
          }
        },
        "required": [
          "faceIndex",
          "flatA",
          "flatB",
          "lineStart",
          "lineEnd",
          "lineMid",
          "bendLineLength",
          "angleDeg",
          "innerRadius",
          "outerRadius",
          "neutralRadius",
          "innerRadiusStart",
          "innerRadiusEnd",
          "outerRadiusStart",
          "outerRadiusEnd",
          "T",
          "K",
          "allowance",
          "bendDeduction",
          "outsideSetback",
          "innerArcLength",
          "outerArcLength",
          "direction",
          "kind",
          "layoutApprox"
        ],
        "additionalProperties": false,
        "description": "One bend: line, angle, radii (inner/outer/neutral and per-end for cones), and developed lengths."
      },
      "UnfoldFaceEntryOutput": {
        "type": "object",
        "properties": {
          "index": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "uid": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "surfaceType": {
            "type": "string",
            "enum": [
              "Plane",
              "Cylinder",
              "Cone",
              "Sphere",
              "Torus",
              "Other"
            ]
          },
          "role": {
            "type": "string",
            "enum": [
              "referenceFlat",
              "otherSideFlat",
              "rim",
              "flat",
              "bend",
              "coneBend",
              "cutout",
              "ignored"
            ]
          },
          "reference": {
            "type": "boolean"
          },
          "opposite": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "area": {
            "type": "number"
          },
          "centroid": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "labelPoint": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "tangentDot": {
            "description": "Bend candidates (cylinder/cone) only: best |flange normal · cylinder radial| over the flanges. ~1 = tangent fold, ~0 = cutout/hole wall.",
            "type": "number"
          },
          "bendOutcome": {
            "description": "How the fold filter classified a bend candidate: 'fold' (kept), 'cutout' (rejected - not tangent / <2 flanges), 'duplicate' (dropped - one fold per flat pair), or '' (a bend face not on the reference side, never evaluated by the filter).",
            "type": "string",
            "enum": [
              "fold",
              "cutout",
              "duplicate",
              ""
            ]
          }
        },
        "required": [
          "index",
          "uid",
          "surfaceType",
          "role",
          "reference",
          "opposite",
          "area",
          "centroid",
          "labelPoint"
        ],
        "additionalProperties": false,
        "description": "A classified source face: stable key (index + BRepGraph uid), role, and label anchors."
      },
      "UnfoldPlacedOutput": {
        "type": "object",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "flat",
              "bendStrip"
            ]
          },
          "sourceFaceIndex": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          },
          "role": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "referenceFlat",
                  "otherSideFlat",
                  "rim",
                  "flat",
                  "bend",
                  "coneBend",
                  "cutout",
                  "ignored"
                ]
              },
              {
                "type": "string",
                "const": "unknown"
              }
            ]
          },
          "flatCentroid": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          }
        },
        "required": [
          "kind",
          "sourceFaceIndex",
          "role",
          "flatCentroid"
        ],
        "additionalProperties": false,
        "description": "A sub-shape laid into the flat compound, mapped back to its source face (compound order)."
      },
      "UnfoldSolidReportErrorOutput": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "const": false
          },
          "error": {
            "type": "string"
          },
          "thickness": {
            "type": "number"
          },
          "kFactor": {
            "type": "number"
          },
          "warning": {
            "type": "string"
          },
          "bends": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UnfoldBendOutput"
            }
          },
          "flatBBox": {
            "type": "array",
            "prefixItems": [
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          },
          "flatArea": {
            "type": "number"
          },
          "referenceArea": {
            "type": "number"
          },
          "totalArea": {
            "type": "number"
          },
          "faces": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UnfoldFaceEntryOutput"
            }
          },
          "placed": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UnfoldPlacedOutput"
            }
          }
        },
        "required": [
          "ok",
          "error"
        ],
        "additionalProperties": false,
        "description": "Returned when a solid cannot be fully unfolded; carries the partial diagnostics gathered before the failure (classified faces with roles, detected thickness, warnings) so the reason can be inspected."
      },
      "UnfoldFaceReport": {
        "anyOf": [
          {
            "type": "object",
            "properties": {
              "ok": {
                "type": "boolean",
                "const": true
              },
              "faceIndex": {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "surfaceType": {
                "type": "string",
                "enum": [
                  "Plane",
                  "Cylinder",
                  "Cone",
                  "Sphere",
                  "Torus",
                  "Other"
                ]
              },
              "developable": {
                "type": "boolean"
              },
              "unfolded": {
                "type": "boolean"
              },
              "area": {
                "type": "number"
              },
              "flatArea": {
                "type": "number"
              },
              "areaError": {
                "type": "number"
              }
            },
            "required": [
              "ok",
              "faceIndex",
              "surfaceType",
              "developable",
              "unfolded",
              "area",
              "flatArea",
              "areaError"
            ],
            "additionalProperties": false
          },
          {
            "$ref": "#/components/schemas/UnfoldReportErrorOutput"
          }
        ],
        "description": "Sheet-metal unfold metrics for a single face (surface type, developability, area before/after)."
      },
      "UnfoldSolidReport": {
        "type": "array",
        "items": {
          "allOf": [
            {
              "$ref": "#/components/schemas/UnfoldSolidReportItemOutput"
            },
            {
              "type": "object",
              "properties": {
                "solidIndex": {
                  "type": "integer",
                  "minimum": -9007199254740991,
                  "maximum": 9007199254740991
                }
              },
              "required": [
                "solidIndex"
              ],
              "additionalProperties": false
            }
          ]
        },
        "description": "Per-solid sheet-metal unfold metrics - one entry per solid in the STEP file (each carries its solidIndex)."
      }
    },
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Scoped API key. Scopes: models, cad, convert, * (wildcard). A key without the required scope receives 403 INSUFFICIENT_SCOPE."
      }
    }
  }
}
