{
  "openapi": "3.1.0",
  "info": {
    "title": "Formlab Asset API",
    "version": "1.0.0",
    "description": "Authenticated creation, retrieval and fast declarative state changes. Jobs run up to 30 minutes on a persistent single-process Node server."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "security": [
    {
      "ApplicationKey": []
    },
    {
      "StudioSession": []
    }
  ],
  "paths": {
    "/api/v1/assets": {
      "get": {
        "operationId": "listAssets",
        "summary": "listAssets",
        "description": "Required scope: assets:read",
        "parameters": [
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 1000000,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssetList"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Quoted numeric asset revision (asset/state responses)."
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createAsset",
        "summary": "createAsset",
        "description": "Required scope: assets:create",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{8,128}$"
            },
            "description": "Durable, account-scoped idempotency. Different content returns 409. Retry failed jobs with a new key."
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted asynchronous job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAsset"
              }
            }
          }
        }
      }
    },
    "/api/v1/jobs/{id}": {
      "get": {
        "operationId": "getJob",
        "summary": "getJob",
        "description": "Required scope: assets:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Quoted numeric asset revision (asset/state responses)."
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/assets/{id}": {
      "get": {
        "operationId": "getAsset",
        "summary": "getAsset",
        "description": "Required scope: assets:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Asset"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Quoted numeric asset revision (asset/state responses)."
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/assets/{id}/prepare": {
      "post": {
        "operationId": "prepareAsset",
        "summary": "prepareAsset",
        "description": "Required scope: assets:create",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{8,128}$"
            },
            "description": "Durable, account-scoped idempotency. Different content returns 409. Retry failed jobs with a new key."
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted asynchronous job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/assets/{id}/state": {
      "get": {
        "operationId": "getState",
        "summary": "getState",
        "description": "Required scope: assets:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-None-Match",
            "in": "header",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssetState"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Quoted numeric asset revision (asset/state responses)."
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "304": {
            "description": "State has not changed."
          }
        }
      },
      "patch": {
        "operationId": "patchState",
        "summary": "patchState",
        "description": "Required scope: assets:state",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "If-Match",
            "in": "header",
            "schema": {
              "type": "string",
              "pattern": "^\"[1-9][0-9]*\"$"
            },
            "description": "ETag from a previous read; mismatched revisions return 412."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AssetState"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Quoted numeric asset revision (asset/state responses)."
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StatePatch"
              }
            }
          }
        }
      }
    },
    "/api/v1/assets/{id}/project": {
      "get": {
        "operationId": "downloadProject",
        "summary": "downloadProject",
        "description": "Required scope: assets:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Project"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Quoted numeric asset revision (asset/state responses)."
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/assets/{id}/model.glb": {
      "get": {
        "operationId": "exportGLB",
        "summary": "exportGLB",
        "description": "Required scope: assets:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "GLB snapshot at requested revision. No Bloom or state runtime. May take several seconds.",
            "content": {
              "model/gltf-binary": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/generate": {
      "post": {
        "operationId": "createAssetAlias",
        "summary": "createAsset",
        "description": "Exact alias of POST /api/v1/assets. Returns 202 Job, not the legacy synchronous {code} response.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{8,128}$"
            },
            "description": "Durable, account-scoped idempotency. Different content returns 409. Retry failed jobs with a new key."
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted asynchronous job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              },
              "Retry-After": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAsset"
              }
            }
          }
        }
      }
    },
    "/api/v1/jobs/{id}/cancel": {
      "post": {
        "operationId": "cancelJob",
        "summary": "Cancel a generation task",
        "description": "Required scope: assets:create. Idempotent; returns terminal job. 409 if already committing (saving).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                },
                "description": "Quoted numeric asset revision (asset/state responses)."
              }
            }
          },
          "400": {
            "description": "Invalid request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Invalid, expired, or revoked key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Missing scope",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Not found or not owned",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Conflict or unprepared asset",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "412": {
            "description": "Revision conflict",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Body exceeds 400 KB",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Invalid state/control",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Busy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Service unavailable",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApplicationKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "fl_<random application key>",
        "description": "Create scoped/revocable keys at /developers; do not use ADMIN_TOKEN."
      },
      "StudioSession": {
        "type": "apiKey",
        "in": "cookie",
        "name": "formlab_session",
        "description": "Same-origin studio only. External clients use ApplicationKey; cross-origin cookie authentication is rejected."
      }
    },
    "schemas": {
      "Style": {
        "type": "string",
        "enum": [
          "none",
          "geometric",
          "soft",
          "lowpoly",
          "metal"
        ],
        "default": "none"
      },
      "Project": {
        "type": "object",
        "properties": {
          "format": {
            "const": "formlab"
          },
          "version": {
            "const": 1
          },
          "projectId": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string",
            "maxLength": 60
          },
          "prompt": {
            "type": "string",
            "maxLength": 1600
          },
          "code": {
            "type": "string",
            "maxLength": 60000
          },
          "style": {
            "$ref": "#/components/schemas/Style"
          },
          "source": {
            "enum": [
              "ai",
              "demo",
              "custom"
            ]
          },
          "transitions": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "guidance": {
                "type": "string",
                "maxLength": 1200
              }
            }
          },
          "bloom": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "guidance": {
                "type": "string",
                "maxLength": 1200
              }
            }
          },
          "activeState": {
            "type": "string"
          },
          "controlValues": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          },
          "settings": {
            "type": "object",
            "description": "Formlab ViewSettings, including material and bloom settings."
          }
        },
        "required": [
          "format",
          "version",
          "title",
          "prompt",
          "code",
          "style",
          "source"
        ]
      },
      "CurrentState": {
        "type": "object",
        "properties": {
          "state": {
            "type": [
              "string",
              "null"
            ]
          },
          "controls": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          },
          "bloom": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "strength": {
                "type": "number",
                "minimum": 0,
                "maximum": 3
              },
              "radius": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "threshold": {
                "type": "number",
                "minimum": 0,
                "maximum": 3
              }
            }
          }
        },
        "required": [
          "state",
          "controls",
          "bloom"
        ]
      },
      "Capabilities": {
        "type": "object",
        "properties": {
          "stats": {
            "type": "object",
            "properties": {
              "meshes": {
                "type": "number"
              },
              "vertices": {
                "type": "number"
              },
              "triangles": {
                "type": "number"
              }
            }
          },
          "states": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "description": {
                  "type": "string"
                }
              }
            }
          },
          "transitions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "from": {
                  "type": "string"
                },
                "to": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "controls": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "min": {
                  "type": "number"
                },
                "max": {
                  "type": "number"
                },
                "step": {
                  "type": "number"
                },
                "initial": {
                  "type": "number"
                },
                "unit": {
                  "type": "string"
                },
                "states": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "active": {
                  "type": "boolean"
                }
              }
            }
          },
          "bloom": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "targets": {
                    "type": "number"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "strength": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 3
                  },
                  "radius": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "threshold": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 3
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "StatePatch": {
        "type": "object",
        "properties": {
          "state": {
            "type": "string"
          },
          "controls": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          },
          "bloom": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "strength": {
                "type": "number",
                "minimum": 0,
                "maximum": 3
              },
              "radius": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "threshold": {
                "type": "number",
                "minimum": 0,
                "maximum": 3
              }
            },
            "additionalProperties": false
          }
        },
        "additionalProperties": false,
        "minProperties": 1
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          }
        },
        "required": [
          "error"
        ]
      },
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "assetId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "enum": [
              "running",
              "succeeded",
              "failed",
              "cancelled"
            ]
          },
          "stage": {
            "enum": [
              "generating",
              "rendering",
              "saving",
              "complete",
              "failed",
              "cancelled"
            ]
          },
          "createdAt": {
            "type": "number"
          },
          "updatedAt": {
            "type": "number"
          },
          "error": {
            "type": "string"
          },
          "links": {
            "type": "object",
            "properties": {
              "self": {
                "type": "string"
              },
              "asset": {
                "type": "string"
              }
            }
          }
        },
        "required": [
          "id",
          "assetId",
          "status",
          "stage"
        ]
      },
      "CreateAsset": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "mode": {
                "const": "ai",
                "default": "ai"
              },
              "prompt": {
                "type": "string",
                "minLength": 4,
                "maxLength": 1600
              },
              "style": {
                "$ref": "#/components/schemas/Style"
              },
              "transitions": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "guidance": {
                    "type": "string",
                    "maxLength": 1200
                  }
                }
              },
              "bloom": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "guidance": {
                    "type": "string",
                    "maxLength": 1200
                  }
                }
              },
              "title": {
                "type": "string",
                "minLength": 1,
                "maxLength": 60
              }
            },
            "required": [
              "prompt"
            ],
            "not": {
              "required": [
                "remix"
              ]
            }
          },
          {
            "type": "object",
            "properties": {
              "mode": {
                "const": "demo"
              },
              "preset": {
                "enum": [
                  "bulb",
                  "chair",
                  "mushroom",
                  "planet",
                  "cactus"
                ]
              },
              "prompt": {
                "type": "string",
                "minLength": 4,
                "maxLength": 1600
              },
              "style": {
                "$ref": "#/components/schemas/Style"
              },
              "transitions": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "guidance": {
                    "type": "string",
                    "maxLength": 1200
                  }
                }
              },
              "bloom": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "guidance": {
                    "type": "string",
                    "maxLength": 1200
                  }
                }
              },
              "title": {
                "type": "string",
                "minLength": 1,
                "maxLength": 60
              }
            },
            "required": [
              "mode",
              "preset"
            ]
          },
          {
            "type": "object",
            "properties": {
              "project": {
                "$ref": "#/components/schemas/Project"
              }
            },
            "required": [
              "project"
            ]
          },
          {
            "type": "object",
            "properties": {
              "mode": {
                "const": "ai",
                "default": "ai"
              },
              "prompt": {
                "type": "string",
                "minLength": 2,
                "maxLength": 1600
              },
              "style": {
                "$ref": "#/components/schemas/Style"
              },
              "transitions": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "guidance": {
                    "type": "string",
                    "maxLength": 1200
                  }
                }
              },
              "bloom": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "guidance": {
                    "type": "string",
                    "maxLength": 1200
                  }
                }
              },
              "title": {
                "type": "string",
                "minLength": 1,
                "maxLength": 60
              },
              "remix": {
                "type": "object",
                "properties": {
                  "assetId": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "assetId"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "prompt",
              "remix"
            ]
          }
        ]
      },
      "Asset": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "createdAt": {
            "type": "number"
          },
          "updatedAt": {
            "type": "number"
          },
          "revision": {
            "type": "integer"
          },
          "prepared": {
            "type": "boolean"
          },
          "project": {
            "$ref": "#/components/schemas/Project"
          },
          "current": {
            "type": "object",
            "properties": {
              "state": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "controls": {
                "type": "object",
                "additionalProperties": {
                  "type": "number"
                }
              },
              "bloom": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "strength": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 3
                  },
                  "radius": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "threshold": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 3
                  }
                }
              }
            },
            "required": [
              "state",
              "controls",
              "bloom"
            ]
          },
          "capabilities": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Capabilities"
              },
              {
                "type": "null"
              }
            ]
          },
          "links": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "required": [
          "id",
          "revision",
          "project",
          "prepared",
          "current"
        ]
      },
      "AssetState": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "updatedAt": {
            "type": "number"
          },
          "revision": {
            "type": "integer"
          },
          "prepared": {
            "type": "boolean"
          },
          "current": {
            "type": "object",
            "properties": {
              "state": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "controls": {
                "type": "object",
                "additionalProperties": {
                  "type": "number"
                }
              },
              "bloom": {
                "type": "object",
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  },
                  "strength": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 3
                  },
                  "radius": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 1
                  },
                  "threshold": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 3
                  }
                }
              }
            },
            "required": [
              "state",
              "controls",
              "bloom"
            ]
          },
          "capabilities": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Capabilities"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "revision",
          "prepared",
          "current"
        ]
      },
      "AssetList": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "createdAt": {
                  "type": "number"
                },
                "updatedAt": {
                  "type": "number"
                },
                "project": {
                  "type": "object",
                  "properties": {
                    "format": {
                      "const": "formlab"
                    },
                    "version": {
                      "const": 1
                    },
                    "projectId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "title": {
                      "type": "string",
                      "maxLength": 60
                    },
                    "prompt": {
                      "type": "string",
                      "maxLength": 1600
                    },
                    "style": {
                      "$ref": "#/components/schemas/Style"
                    },
                    "source": {
                      "enum": [
                        "ai",
                        "demo",
                        "custom"
                      ]
                    },
                    "transitions": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "guidance": {
                          "type": "string",
                          "maxLength": 1200
                        }
                      }
                    },
                    "bloom": {
                      "type": "object",
                      "properties": {
                        "enabled": {
                          "type": "boolean"
                        },
                        "guidance": {
                          "type": "string",
                          "maxLength": 1200
                        }
                      }
                    },
                    "activeState": {
                      "type": "string"
                    },
                    "controlValues": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "number"
                      }
                    }
                  },
                  "required": [
                    "format",
                    "version",
                    "title",
                    "prompt",
                    "style",
                    "source"
                  ]
                },
                "thumbnail": {
                  "type": "string"
                }
              }
            }
          },
          "nextOffset": {
            "type": [
              "integer",
              "null"
            ]
          }
        },
        "required": [
          "items",
          "nextOffset"
        ]
      }
    }
  }
}
