{
 "components": {
  "schemas": {
   "CompareCaseBody": {
    "additionalProperties": false,
    "description": "One process in a TEA comparison. When simulation_results is omitted or empty\nthe endpoint simulates the flowsheet itself (same path as /simulate-separation)\nbefore costing it, so saved projects and generated routes can be compared\nwithout a client-side simulation round trip.",
    "properties": {
     "calculated_purity": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "null"
       }
      ],
      "title": "Calculated Purity"
     },
     "calculated_yield": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "null"
       }
      ],
      "title": "Calculated Yield"
     },
     "label": {
      "title": "Label",
      "type": "string"
     },
     "simulation_results": {
      "anyOf": [
       {
        "items": {
         "additionalProperties": true,
         "type": "object"
        },
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Simulation Results"
     },
     "steps": {
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "minItems": 1,
      "title": "Steps",
      "type": "array"
     },
     "stream": {
      "additionalProperties": true,
      "title": "Stream",
      "type": "object"
     },
     "target_products": {
      "items": {
       "type": "string"
      },
      "minItems": 1,
      "title": "Target Products",
      "type": "array"
     },
     "thermal_duties": {
      "anyOf": [
       {
        "items": {
         "anyOf": [
          {
           "additionalProperties": true,
           "type": "object"
          },
          {
           "type": "null"
          }
         ]
        },
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Thermal Duties"
     }
    },
    "required": [
     "label",
     "stream",
     "steps",
     "target_products"
    ],
    "title": "CompareCaseBody",
    "type": "object"
   },
   "EvolutionaryGenerateBody": {
    "additionalProperties": false,
    "description": "Route generation, on the SAME economic basis as everything else.\n\nThe generator used to cost on `calculate_detailed_tea`'s own defaults - facility_type\n\"chemical\", 8000 h, $100/kg, default tariffs - while the panel resolved pharma_gmp via\ncp.infer_facility_type for the same flowsheet: 1031.91 $/kg against 6410.01, and\n12.73M against 17.11M of capital. The tier difference has to be the fidelity of the\nsimulation, never a different price book.\n\nSince 2026-08-26 neither side infers: the grade is stated by the caller or the\nrequest is refused (_require_facility_type).",
    "properties": {
     "annual_operating_hours": {
      "default": 8000,
      "maximum": 8760.0,
      "minimum": 1.0,
      "title": "Annual Operating Hours",
      "type": "integer"
     },
     "bioreactor_id": {
      "title": "Bioreactor Id",
      "type": "string"
     },
     "bioreactor_params": {
      "additionalProperties": true,
      "default": {},
      "title": "Bioreactor Params",
      "type": "object"
     },
     "cooling_water_price_usd_per_m3": {
      "default": 0.03,
      "exclusiveMinimum": 0.0,
      "maximum": 10.0,
      "title": "Cooling Water Price Usd Per M3",
      "type": "number"
     },
     "electricity_price_usd_per_kWh": {
      "default": 0.1,
      "exclusiveMinimum": 0.0,
      "maximum": 2.0,
      "title": "Electricity Price Usd Per Kwh",
      "type": "number"
     },
     "excluded_operations": {
      "default": [],
      "items": {
       "type": "string"
      },
      "title": "Excluded Operations",
      "type": "array"
     },
     "facility_type": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Facility Type"
     },
     "generations": {
      "default": 10,
      "maximum": 50.0,
      "title": "Generations",
      "type": "integer"
     },
     "labor_profile": {
      "default": "us_food",
      "title": "Labor Profile",
      "type": "string"
     },
     "min_purity": {
      "default": 0.7,
      "title": "Min Purity",
      "type": "number"
     },
     "min_yield": {
      "default": 0.1,
      "title": "Min Yield",
      "type": "number"
     },
     "optimization_goal": {
      "default": "balanced",
      "title": "Optimization Goal",
      "type": "string"
     },
     "population_size": {
      "default": 1000,
      "maximum": 5000.0,
      "title": "Population Size",
      "type": "integer"
     },
     "product_selling_price_usd_per_kg": {
      "default": 100.0,
      "maximum": 10000000.0,
      "minimum": 0.0,
      "title": "Product Selling Price Usd Per Kg",
      "type": "number"
     },
     "removed_products": {
      "default": [],
      "items": {
       "type": "string"
      },
      "title": "Removed Products",
      "type": "array"
     },
     "seed": {
      "title": "Seed",
      "type": "integer"
     },
     "seed_steps": {
      "anyOf": [
       {
        "items": {
         "$ref": "#/components/schemas/GeneratorSeedStep"
        },
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Seed Steps"
     },
     "seed_suffix_steps": {
      "anyOf": [
       {
        "items": {
         "$ref": "#/components/schemas/GeneratorSeedStep"
        },
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Seed Suffix Steps"
     },
     "steam_price_usd_per_GJ": {
      "default": 18.0,
      "exclusiveMinimum": 0.0,
      "maximum": 200.0,
      "title": "Steam Price Usd Per Gj",
      "type": "number"
     },
     "stream": {
      "additionalProperties": true,
      "title": "Stream",
      "type": "object"
     },
     "target_products": {
      "items": {
       "type": "string"
      },
      "title": "Target Products",
      "type": "array"
     },
     "top_n": {
      "default": 5000,
      "maximum": 10000.0,
      "title": "Top N",
      "type": "integer"
     }
    },
    "required": [
     "stream",
     "target_products"
    ],
    "title": "EvolutionaryGenerateBody",
    "type": "object"
   },
   "FermentationPreflightBody": {
    "additionalProperties": false,
    "description": "The bioreactor alone, before any downstream train exists.",
    "properties": {
     "bioreactor_id": {
      "title": "Bioreactor Id",
      "type": "string"
     },
     "bioreactor_params": {
      "additionalProperties": true,
      "default": {},
      "title": "Bioreactor Params",
      "type": "object"
     },
     "stream": {
      "additionalProperties": true,
      "title": "Stream",
      "type": "object"
     },
     "target_products": {
      "default": [],
      "items": {
       "type": "string"
      },
      "title": "Target Products",
      "type": "array"
     }
    },
    "required": [
     "stream",
     "bioreactor_id"
    ],
    "title": "FermentationPreflightBody",
    "type": "object"
   },
   "GeneratorSeedStep": {
    "additionalProperties": false,
    "description": "One kept canvas step for continue-from-canvas generation.\n\noutlet_handle accepts the canvas vocabulary too: single-output nodes wire\nthrough 'output', which the generator normalises to 'light'.\nproduct_outlets records the DRAWN product deliveries on this step\n({outlet_handle: product_name}) - the canvas is the authority on which\nproduct leaves where; simulator exit marks are opinions.",
    "properties": {
     "outlet_handle": {
      "default": "light",
      "title": "Outlet Handle",
      "type": "string"
     },
     "parameters": {
      "additionalProperties": true,
      "default": {},
      "title": "Parameters",
      "type": "object"
     },
     "product_outlets": {
      "additionalProperties": true,
      "default": {},
      "title": "Product Outlets",
      "type": "object"
     },
     "unit_operation_id": {
      "title": "Unit Operation Id",
      "type": "string"
     }
    },
    "required": [
     "unit_operation_id"
    ],
    "title": "GeneratorSeedStep",
    "type": "object"
   },
   "GoalSeekBody": {
    "additionalProperties": false,
    "description": "Same feed and steps the simulation posted, goal-sought against a cost target.\n\nStage 1 searches the operating (and optionally equipment) parameters for settings\nthat reach `target_cogs_per_kg`, stopping early when they do. If they cannot, stage 2\nopens the model-role calibrations and the feed titre one at a time and reports the\nvalue of each at which the target becomes reachable - R&D requirements, not settings.",
    "properties": {
     "annual_operating_hours": {
      "default": 8000,
      "maximum": 8760.0,
      "minimum": 1.0,
      "title": "Annual Operating Hours",
      "type": "integer"
     },
     "cooling_water_price_usd_per_m3": {
      "default": 0.03,
      "exclusiveMinimum": 0.0,
      "maximum": 10.0,
      "title": "Cooling Water Price Usd Per M3",
      "type": "number"
     },
     "electricity_price_usd_per_kWh": {
      "default": 0.1,
      "exclusiveMinimum": 0.0,
      "maximum": 2.0,
      "title": "Electricity Price Usd Per Kwh",
      "type": "number"
     },
     "facility_type": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Facility Type"
     },
     "labor_profile": {
      "default": "us_food",
      "title": "Labor Profile",
      "type": "string"
     },
     "max_evaluations": {
      "default": 6000,
      "maximum": 20000.0,
      "minimum": 100.0,
      "title": "Max Evaluations",
      "type": "integer"
     },
     "max_multiplier": {
      "default": 10.0,
      "exclusiveMinimum": 1.0,
      "maximum": 100.0,
      "title": "Max Multiplier",
      "type": "number"
     },
     "min_purity": {
      "default": 0.0,
      "maximum": 1.0,
      "minimum": 0.0,
      "title": "Min Purity",
      "type": "number"
     },
     "min_yield": {
      "default": 0.0,
      "maximum": 1.0,
      "minimum": 0.0,
      "title": "Min Yield",
      "type": "number"
     },
     "optimize_feed_flow": {
      "default": true,
      "title": "Optimize Feed Flow",
      "type": "boolean"
     },
     "product_selling_price_usd_per_kg": {
      "default": 100.0,
      "maximum": 10000000.0,
      "minimum": 0.0,
      "title": "Product Selling Price Usd Per Kg",
      "type": "number"
     },
     "roles": {
      "items": {
       "type": "string"
      },
      "title": "Roles",
      "type": "array"
     },
     "seed": {
      "default": 1,
      "title": "Seed",
      "type": "integer"
     },
     "skip_steps": {
      "items": {
       "type": "integer"
      },
      "title": "Skip Steps",
      "type": "array"
     },
     "steam_price_usd_per_GJ": {
      "default": 18.0,
      "exclusiveMinimum": 0.0,
      "maximum": 200.0,
      "title": "Steam Price Usd Per Gj",
      "type": "number"
     },
     "steps": {
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "title": "Steps",
      "type": "array"
     },
     "stream": {
      "additionalProperties": true,
      "title": "Stream",
      "type": "object"
     },
     "target_cogs_per_kg": {
      "exclusiveMinimum": 0.0,
      "title": "Target Cogs Per Kg",
      "type": "number"
     },
     "target_products": {
      "items": {
       "type": "string"
      },
      "title": "Target Products",
      "type": "array"
     }
    },
    "required": [
     "stream",
     "steps",
     "target_products",
     "target_cogs_per_kg"
    ],
    "title": "GoalSeekBody",
    "type": "object"
   },
   "HTTPValidationError": {
    "properties": {
     "detail": {
      "items": {
       "$ref": "#/components/schemas/ValidationError"
      },
      "title": "Detail",
      "type": "array"
     }
    },
    "title": "HTTPValidationError",
    "type": "object"
   },
   "InvestorMetricsBody": {
    "additionalProperties": false,
    "description": "Request body for investor metrics",
    "properties": {
     "annual_opex": {
      "title": "Annual Opex",
      "type": "number"
     },
     "annual_revenue": {
      "title": "Annual Revenue",
      "type": "number"
     },
     "depreciation": {
      "title": "Depreciation",
      "type": "number"
     },
     "project_life": {
      "default": 10,
      "title": "Project Life",
      "type": "integer"
     },
     "tax_rate": {
      "default": 0.21,
      "title": "Tax Rate",
      "type": "number"
     },
     "tci": {
      "title": "Tci",
      "type": "number"
     },
     "wacc": {
      "default": 0.12,
      "title": "Wacc",
      "type": "number"
     }
    },
    "required": [
     "tci",
     "annual_opex",
     "annual_revenue",
     "depreciation"
    ],
    "title": "InvestorMetricsBody",
    "type": "object"
   },
   "OptimizeFlowsheetBody": {
    "additionalProperties": false,
    "description": "Request body for parameter optimisation of a flowsheet the user has already chosen.\n\nMirrors SimulateBody's shape (stream + steps carrying their own parameters), so the\ncanvas can post exactly what it would post to simulate, plus the specs to hit.",
    "properties": {
     "annual_operating_hours": {
      "default": 8000,
      "maximum": 8760.0,
      "minimum": 1.0,
      "title": "Annual Operating Hours",
      "type": "integer"
     },
     "cooling_water_price_usd_per_m3": {
      "default": 0.03,
      "exclusiveMinimum": 0.0,
      "maximum": 10.0,
      "title": "Cooling Water Price Usd Per M3",
      "type": "number"
     },
     "electricity_price_usd_per_kWh": {
      "default": 0.1,
      "exclusiveMinimum": 0.0,
      "maximum": 2.0,
      "title": "Electricity Price Usd Per Kwh",
      "type": "number"
     },
     "facility_type": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Facility Type"
     },
     "feed_flow_max_multiplier": {
      "default": 4.0,
      "maximum": 100.0,
      "minimum": 1.0,
      "title": "Feed Flow Max Multiplier",
      "type": "number"
     },
     "feed_flow_min_multiplier": {
      "default": 0.25,
      "exclusiveMinimum": 0.0,
      "maximum": 1.0,
      "title": "Feed Flow Min Multiplier",
      "type": "number"
     },
     "hold_baseline_economics": {
      "default": true,
      "title": "Hold Baseline Economics",
      "type": "boolean"
     },
     "hold_baseline_production": {
      "default": true,
      "title": "Hold Baseline Production",
      "type": "boolean"
     },
     "hold_baseline_quality": {
      "default": true,
      "title": "Hold Baseline Quality",
      "type": "boolean"
     },
     "labor_profile": {
      "default": "us_food",
      "title": "Labor Profile",
      "type": "string"
     },
     "max_evaluations": {
      "default": 6000,
      "maximum": 20000.0,
      "minimum": 100.0,
      "title": "Max Evaluations",
      "type": "integer"
     },
     "min_purity": {
      "default": 0.0,
      "maximum": 1.0,
      "minimum": 0.0,
      "title": "Min Purity",
      "type": "number"
     },
     "min_yield": {
      "default": 0.0,
      "maximum": 1.0,
      "minimum": 0.0,
      "title": "Min Yield",
      "type": "number"
     },
     "objective": {
      "default": "min_cogs",
      "title": "Objective",
      "type": "string"
     },
     "optimize_feed_flow": {
      "default": false,
      "title": "Optimize Feed Flow",
      "type": "boolean"
     },
     "pareto": {
      "default": false,
      "title": "Pareto",
      "type": "boolean"
     },
     "pareto_purity_levels": {
      "anyOf": [
       {
        "items": {
         "type": "number"
        },
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Pareto Purity Levels"
     },
     "product_selling_price_usd_per_kg": {
      "default": 100.0,
      "maximum": 10000000.0,
      "minimum": 0.0,
      "title": "Product Selling Price Usd Per Kg",
      "type": "number"
     },
     "roles": {
      "items": {
       "type": "string"
      },
      "title": "Roles",
      "type": "array"
     },
     "screen_first": {
      "default": true,
      "title": "Screen First",
      "type": "boolean"
     },
     "seed": {
      "default": 1,
      "title": "Seed",
      "type": "integer"
     },
     "skip_steps": {
      "items": {
       "type": "integer"
      },
      "title": "Skip Steps",
      "type": "array"
     },
     "steam_price_usd_per_GJ": {
      "default": 18.0,
      "exclusiveMinimum": 0.0,
      "maximum": 200.0,
      "title": "Steam Price Usd Per Gj",
      "type": "number"
     },
     "steps": {
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "title": "Steps",
      "type": "array"
     },
     "stream": {
      "additionalProperties": true,
      "title": "Stream",
      "type": "object"
     },
     "target_products": {
      "items": {
       "type": "string"
      },
      "title": "Target Products",
      "type": "array"
     }
    },
    "required": [
     "stream",
     "steps",
     "target_products"
    ],
    "title": "OptimizeFlowsheetBody",
    "type": "object"
   },
   "ScaleAnalysisBody": {
    "additionalProperties": false,
    "description": "Request body for scale-up analysis",
    "properties": {
     "annual_operating_hours": {
      "default": 8000,
      "maximum": 8760.0,
      "minimum": 1.0,
      "title": "Annual Operating Hours",
      "type": "integer"
     },
     "calculated_purity": {
      "default": 0.85,
      "title": "Calculated Purity",
      "type": "number"
     },
     "calculated_yield": {
      "default": 0.7,
      "title": "Calculated Yield",
      "type": "number"
     },
     "cooling_water_price_usd_per_m3": {
      "default": 0.03,
      "exclusiveMinimum": 0.0,
      "maximum": 10.0,
      "title": "Cooling Water Price Usd Per M3",
      "type": "number"
     },
     "electricity_price_usd_per_kWh": {
      "default": 0.1,
      "exclusiveMinimum": 0.0,
      "maximum": 2.0,
      "title": "Electricity Price Usd Per Kwh",
      "type": "number"
     },
     "facility_type": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Facility Type"
     },
     "feed_basis": {
      "default": "auto",
      "enum": [
       "auto",
       "internal_broth",
       "purchased"
      ],
      "title": "Feed Basis",
      "type": "string"
     },
     "labor_profile": {
      "default": "us_food",
      "title": "Labor Profile",
      "type": "string"
     },
     "product_selling_price_usd_per_kg": {
      "default": 100.0,
      "maximum": 10000000.0,
      "minimum": 0.0,
      "title": "Product Selling Price Usd Per Kg",
      "type": "number"
     },
     "raw_material_cost_multiplier": {
      "default": 1.0,
      "maximum": 100.0,
      "minimum": 0.0,
      "title": "Raw Material Cost Multiplier",
      "type": "number"
     },
     "scale_points": {
      "default": [
       100.0,
       250.0,
       500.0,
       1000.0,
       2500.0,
       5000.0,
       10000.0
      ],
      "items": {
       "type": "number"
      },
      "title": "Scale Points",
      "type": "array"
     },
     "simulation_results": {
      "default": [],
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "title": "Simulation Results",
      "type": "array"
     },
     "steam_price_usd_per_GJ": {
      "default": 18.0,
      "exclusiveMinimum": 0.0,
      "maximum": 200.0,
      "title": "Steam Price Usd Per Gj",
      "type": "number"
     },
     "steps": {
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "title": "Steps",
      "type": "array"
     },
     "stream": {
      "additionalProperties": true,
      "title": "Stream",
      "type": "object"
     },
     "target_products": {
      "items": {
       "type": "string"
      },
      "title": "Target Products",
      "type": "array"
     }
    },
    "required": [
     "stream",
     "steps",
     "target_products"
    ],
    "title": "ScaleAnalysisBody",
    "type": "object"
   },
   "SensitivityBody": {
    "additionalProperties": false,
    "description": "Same feed and steps the simulation posted, replayed for a tornado analysis.\n\n`modes` selects which chart(s) to compute:\n  operating    what each dial an operator could turn is worth (the sensitivity\n               screen across engineering bounds),\n  uncertainty  which assumptions dominate the error bar (model calibrations,\n               economics and feed titre swept +/- `variation`).",
    "properties": {
     "annual_operating_hours": {
      "default": 8000,
      "maximum": 8760.0,
      "minimum": 1.0,
      "title": "Annual Operating Hours",
      "type": "integer"
     },
     "cooling_water_price_usd_per_m3": {
      "default": 0.03,
      "exclusiveMinimum": 0.0,
      "maximum": 10.0,
      "title": "Cooling Water Price Usd Per M3",
      "type": "number"
     },
     "electricity_price_usd_per_kWh": {
      "default": 0.1,
      "exclusiveMinimum": 0.0,
      "maximum": 2.0,
      "title": "Electricity Price Usd Per Kwh",
      "type": "number"
     },
     "facility_type": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Facility Type"
     },
     "labor_profile": {
      "default": "us_food",
      "title": "Labor Profile",
      "type": "string"
     },
     "modes": {
      "items": {
       "type": "string"
      },
      "title": "Modes",
      "type": "array"
     },
     "objective": {
      "default": "min_cogs",
      "title": "Objective",
      "type": "string"
     },
     "product_selling_price_usd_per_kg": {
      "default": 100.0,
      "maximum": 10000000.0,
      "minimum": 0.0,
      "title": "Product Selling Price Usd Per Kg",
      "type": "number"
     },
     "samples_per_variable": {
      "default": 5,
      "maximum": 11.0,
      "minimum": 3.0,
      "title": "Samples Per Variable",
      "type": "integer"
     },
     "skip_steps": {
      "items": {
       "type": "integer"
      },
      "title": "Skip Steps",
      "type": "array"
     },
     "steam_price_usd_per_GJ": {
      "default": 18.0,
      "exclusiveMinimum": 0.0,
      "maximum": 200.0,
      "title": "Steam Price Usd Per Gj",
      "type": "number"
     },
     "steps": {
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "title": "Steps",
      "type": "array"
     },
     "stream": {
      "additionalProperties": true,
      "title": "Stream",
      "type": "object"
     },
     "target_products": {
      "items": {
       "type": "string"
      },
      "title": "Target Products",
      "type": "array"
     },
     "variation": {
      "default": 0.3,
      "exclusiveMinimum": 0.0,
      "maximum": 0.9,
      "title": "Variation",
      "type": "number"
     }
    },
    "required": [
     "stream",
     "steps",
     "target_products"
    ],
    "title": "SensitivityBody",
    "type": "object"
   },
   "SimulateBody": {
    "additionalProperties": false,
    "description": "Feed plus steps, as the canvas posts them.\n\nInherits the economic basis only for `annual_energy_cost_usd` in the response, which\nwas `total_energy_kw * 8000 * 0.10` - hardcoded hours and a fourth copy of\nDEFAULT_ELECTRICITY_USD_PER_KWH, honouring neither the user's operating hours nor\ntheir tariff. Every field defaults to the house basis, so an old client sending only\nstream/steps/target_products gets exactly the numbers it did before.",
    "properties": {
     "annual_operating_hours": {
      "default": 8000,
      "maximum": 8760.0,
      "minimum": 1.0,
      "title": "Annual Operating Hours",
      "type": "integer"
     },
     "cooling_water_price_usd_per_m3": {
      "default": 0.03,
      "exclusiveMinimum": 0.0,
      "maximum": 10.0,
      "title": "Cooling Water Price Usd Per M3",
      "type": "number"
     },
     "electricity_price_usd_per_kWh": {
      "default": 0.1,
      "exclusiveMinimum": 0.0,
      "maximum": 2.0,
      "title": "Electricity Price Usd Per Kwh",
      "type": "number"
     },
     "facility_type": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Facility Type"
     },
     "labor_profile": {
      "default": "us_food",
      "title": "Labor Profile",
      "type": "string"
     },
     "product_selling_price_usd_per_kg": {
      "default": 100.0,
      "maximum": 10000000.0,
      "minimum": 0.0,
      "title": "Product Selling Price Usd Per Kg",
      "type": "number"
     },
     "steam_price_usd_per_GJ": {
      "default": 18.0,
      "exclusiveMinimum": 0.0,
      "maximum": 200.0,
      "title": "Steam Price Usd Per Gj",
      "type": "number"
     },
     "steps": {
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "title": "Steps",
      "type": "array"
     },
     "stream": {
      "additionalProperties": true,
      "title": "Stream",
      "type": "object"
     },
     "target_products": {
      "items": {
       "type": "string"
      },
      "title": "Target Products",
      "type": "array"
     }
    },
    "required": [
     "stream",
     "steps",
     "target_products"
    ],
    "title": "SimulateBody",
    "type": "object"
   },
   "TEABody": {
    "additionalProperties": false,
    "description": "Request body for detailed techno-economic analysis",
    "properties": {
     "annual_operating_hours": {
      "default": 8000,
      "maximum": 8760.0,
      "minimum": 1.0,
      "title": "Annual Operating Hours",
      "type": "integer"
     },
     "calculated_purity": {
      "title": "Calculated Purity",
      "type": "number"
     },
     "calculated_yield": {
      "title": "Calculated Yield",
      "type": "number"
     },
     "cooling_water_price_usd_per_m3": {
      "default": 0.03,
      "exclusiveMinimum": 0.0,
      "maximum": 10.0,
      "title": "Cooling Water Price Usd Per M3",
      "type": "number"
     },
     "electricity_price_usd_per_kWh": {
      "default": 0.1,
      "exclusiveMinimum": 0.0,
      "maximum": 2.0,
      "title": "Electricity Price Usd Per Kwh",
      "type": "number"
     },
     "facility_type": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Facility Type"
     },
     "feed_basis": {
      "default": "auto",
      "enum": [
       "auto",
       "internal_broth",
       "purchased"
      ],
      "title": "Feed Basis",
      "type": "string"
     },
     "labor_profile": {
      "default": "us_food",
      "title": "Labor Profile",
      "type": "string"
     },
     "product_selling_price_usd_per_kg": {
      "default": 100.0,
      "maximum": 10000000.0,
      "minimum": 0.0,
      "title": "Product Selling Price Usd Per Kg",
      "type": "number"
     },
     "raw_material_cost_multiplier": {
      "default": 1.0,
      "maximum": 100.0,
      "minimum": 0.0,
      "title": "Raw Material Cost Multiplier",
      "type": "number"
     },
     "simulation_results": {
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "title": "Simulation Results",
      "type": "array"
     },
     "steam_price_usd_per_GJ": {
      "default": 18.0,
      "exclusiveMinimum": 0.0,
      "maximum": 200.0,
      "title": "Steam Price Usd Per Gj",
      "type": "number"
     },
     "steps": {
      "items": {
       "additionalProperties": true,
       "type": "object"
      },
      "title": "Steps",
      "type": "array"
     },
     "stream": {
      "additionalProperties": true,
      "title": "Stream",
      "type": "object"
     },
     "target_products": {
      "items": {
       "type": "string"
      },
      "title": "Target Products",
      "type": "array"
     },
     "thermal_duties": {
      "anyOf": [
       {
        "items": {
         "anyOf": [
          {
           "additionalProperties": true,
           "type": "object"
          },
          {
           "type": "null"
          }
         ]
        },
        "type": "array"
       },
       {
        "type": "null"
       }
      ],
      "title": "Thermal Duties"
     }
    },
    "required": [
     "stream",
     "steps",
     "target_products",
     "simulation_results",
     "calculated_yield",
     "calculated_purity"
    ],
    "title": "TEABody",
    "type": "object"
   },
   "TEACompareBody": {
    "additionalProperties": false,
    "description": "2-3 processes plus ONE shared economic basis. Every case is recomputed on\nthat basis - stored TEA results are never joined, because two stored results\nalmost never share operating hours, facility type and prices.",
    "properties": {
     "annual_operating_hours": {
      "default": 8000,
      "maximum": 8760.0,
      "minimum": 1.0,
      "title": "Annual Operating Hours",
      "type": "integer"
     },
     "cases": {
      "items": {
       "$ref": "#/components/schemas/CompareCaseBody"
      },
      "maxItems": 3,
      "minItems": 2,
      "title": "Cases",
      "type": "array"
     },
     "cooling_water_price_usd_per_m3": {
      "default": 0.03,
      "exclusiveMinimum": 0.0,
      "maximum": 10.0,
      "title": "Cooling Water Price Usd Per M3",
      "type": "number"
     },
     "electricity_price_usd_per_kWh": {
      "default": 0.1,
      "exclusiveMinimum": 0.0,
      "maximum": 2.0,
      "title": "Electricity Price Usd Per Kwh",
      "type": "number"
     },
     "facility_type": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Facility Type"
     },
     "labor_profile": {
      "default": "us_food",
      "title": "Labor Profile",
      "type": "string"
     },
     "product_selling_price_usd_per_kg": {
      "default": 100.0,
      "maximum": 10000000.0,
      "minimum": 0.0,
      "title": "Product Selling Price Usd Per Kg",
      "type": "number"
     },
     "project_life": {
      "default": 10,
      "maximum": 50.0,
      "minimum": 1.0,
      "title": "Project Life",
      "type": "integer"
     },
     "raw_material_cost_multiplier": {
      "default": 1.0,
      "maximum": 100.0,
      "minimum": 0.0,
      "title": "Raw Material Cost Multiplier",
      "type": "number"
     },
     "steam_price_usd_per_GJ": {
      "default": 18.0,
      "exclusiveMinimum": 0.0,
      "maximum": 200.0,
      "title": "Steam Price Usd Per Gj",
      "type": "number"
     },
     "tax_rate": {
      "default": 0.21,
      "exclusiveMaximum": 1.0,
      "minimum": 0.0,
      "title": "Tax Rate",
      "type": "number"
     },
     "wacc": {
      "default": 0.12,
      "exclusiveMinimum": -1.0,
      "maximum": 1.0,
      "title": "Wacc",
      "type": "number"
     }
    },
    "required": [
     "cases"
    ],
    "title": "TEACompareBody",
    "type": "object"
   },
   "ValidationError": {
    "properties": {
     "ctx": {
      "title": "Context",
      "type": "object"
     },
     "input": {
      "title": "Input"
     },
     "loc": {
      "items": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "integer"
        }
       ]
      },
      "title": "Location",
      "type": "array"
     },
     "msg": {
      "title": "Message",
      "type": "string"
     },
     "type": {
      "title": "Error Type",
      "type": "string"
     }
    },
    "required": [
     "loc",
     "msg",
     "type"
    ],
    "title": "ValidationError",
    "type": "object"
   }
  },
  "securitySchemes": {
   "bearerAuth": {
    "bearerFormat": "JWT",
    "description": "Supabase-issued access token for an untangle.bio account.",
    "scheme": "bearer",
    "type": "http"
   }
  }
 },
 "info": {
  "contact": {
   "url": "https://untangle.bio/docs.html"
  },
  "description": "The untangle.bio bioprocess engine: purification-route generation, separation and fermentation simulation, and techno-economic analysis (TEA).\n\nThis spec covers the public engine surface only. Authentication: every request needs a Supabase-issued bearer JWT for an untangle.bio account (`Authorization: Bearer <token>`); connector access is part of the Academic and Pro plans.\n\nMost AI assistants should prefer the MCP connector at https://mcp.untangle.bio/mcp - same engine, OAuth sign-in, result presentation built in. The complete MCP tool reference is at https://untangle.bio/llms-full.txt\n",
  "title": "untangle.bio engine API",
  "version": "1.0.0"
 },
 "openapi": "3.1.0",
 "paths": {
  "/api/analyze-sensitivity": {
   "post": {
    "description": "Tornado analysis of the flowsheet as it stands. Nothing is searched and nothing is\nwritten back: each bar reports how far the objective moves when one input is swept\nwhile everything else stays at its current value.",
    "operationId": "analyze_sensitivity_api_analyze_sensitivity_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SensitivityBody"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Analyze Sensitivity"
   }
  },
  "/api/generate-processes-evolutionary": {
   "post": {
    "description": "Generate optimal process routes using evolutionary algorithm.\nSearches for routes that maximize yield x purity while meeting minimum purity and yield constraints.",
    "operationId": "generate_processes_evolutionary_route_api_generate_processes_evolutionary_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/EvolutionaryGenerateBody"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Generate Processes Evolutionary Route"
   }
  },
  "/api/goal-seek": {
   "post": {
    "description": "\"Can this flowsheet reach X $/kg, and if not, what would have to be true?\"\n\nTopology is fixed. Stage 1's answer is apply-able operating settings; stage 2's answer\nis a list of single-lever requirements (titre, resin capacity, fouling factor, ...)\nthat are explicitly NOT written back to the canvas.",
    "operationId": "goal_seek_api_goal_seek_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/GoalSeekBody"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Goal Seek"
   }
  },
  "/api/mixtures": {
   "get": {
    "description": "Built-in mixtures: named compositions of several molecules at fixed dry-basis\nmass fractions.\n\nEach member is stored in the JSON as nothing but a molecule id and a mass\nfraction, and is RESOLVED HERE against molecule_database.json so the response\ncarries that molecule's own molecular weight, density, pI, solubility and log P.\nThat is the whole point of the endpoint: a mixture must never reach a client - or\na mass balance - as one averaged pseudo-component. The members span the real\nproperty range (WPC80 runs 14 kDa to 150 kDa; citrus peel 180 Da to 162 kDa),\nand every separation model in the app is written against a single species, so it\nhas to see the species rather than their average.\n\nA member whose id is not in the molecule database is DROPPED rather than served\nwith empty properties, and the mixture records what was dropped. A component\nwith no molecular weight or density cannot close a mass balance, and serving one\nsilently is worse than serving a shorter list.",
    "operationId": "get_mixture_database_api_mixtures_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Get Mixture Database"
   }
  },
  "/api/molecules": {
   "get": {
    "description": "Get the comprehensive molecule database with properties for biotech applications.\nReturns molecules organized by category (proteins, sugars, acids, etc.).\n\nEvery value in the response is a LIST of molecules. Six components across the\napp iterate this response as category -> molecules and none of them check;\n`Object.keys(...).filter(c => molecules[c].length > 0)` happily admits a\nstring, because strings have a length, and the picker then sorts a category\nof characters by `.name` and takes the whole app down with it. A single\ndocumentation key added to the JSON did exactly that. Non-list values are\ndropped here so the shape the clients assume is the shape they get, whatever\nanyone later adds to the file.",
    "operationId": "get_molecule_database_api_molecules_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Get Molecule Database"
   }
  },
  "/api/optimization-objectives": {
   "get": {
    "description": "The objectives a search may actually be pointed at.\n\nServed for the same reason GET /api/tea/profiles is: the objective registry was the\nlast catalogue in the app mirrored by hand. OptimizeFlowsheetBody's docstring claimed\nthis endpoint existed, OptimizeFlowsheetDialog.tsx warns that \"keys must match, or the\nbackend rejects the request\", and docs/knowledge/project-optimize-objectives.md points\nat it - while the route returned 404 and the frontend list was a hardcoded copy.\n\n`degenerate_alone` marks the objectives that are minimised by making no product, so a\ncaller can show the quality-floor warning without knowing which ones those are.",
    "operationId": "optimization_objectives_api_optimization_objectives_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Optimization Objectives"
   }
  },
  "/api/optimize-flowsheet": {
   "post": {
    "description": "Find the best way to run a flowsheet the user has already chosen.\n\nThe topology is fixed: no step is added, removed or reordered. Only parameters that\n`design_space.py` classifies as tunable are moved, so the search cannot make the process\nlook cheap by assuming a better resin or membrane than the one being costed.\n\nReturns the optimised parameters per step, ready to write straight back onto the canvas,\ntogether with the baseline they beat and a note on any value that landed on a bound.",
    "operationId": "optimize_flowsheet_api_optimize_flowsheet_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/OptimizeFlowsheetBody"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Optimize Flowsheet"
   }
  },
  "/api/organisms": {
   "get": {
    "description": "The fermentation host library, as the bioreactor parameter panel needs it:\norganism key -> the value every `host_*` field falls back to when left blank.\n\nThe panel used to print \"library value\" in each of those boxes, which told the\nuser that a number existed but not what it was, so overriding one meant guessing\nfirst. Serving the library from the same module the simulation resolves hosts\nthrough is what keeps the printed number and the simulated number the same one.",
    "operationId": "get_organism_library_api_organisms_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Get Organism Library"
   }
  },
  "/api/reaction-templates": {
   "get": {
    "description": "The M3 reaction-template register, as the frontend needs it: template id\n-> name, substrates, ordered steps (chemistry + enzyme), X band, position\nrules, demand rule and source. Served from the SAME validated register\nthe generator fires from (services/reaction_templates.catalog), so a\npicker never grows a second hand-maintained options list the way the\nenzyme select did.",
    "operationId": "get_reaction_templates_api_reaction_templates_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Get Reaction Templates"
   }
  },
  "/api/simulate-fermentation": {
   "post": {
    "description": "Run only the fermentation and return the broth it produces.\n\nRoute generation treats the bioreactor as a fixed first step and searches\ndownstream of whatever it makes, so the broth is the real input to the search\n- every candidate route, every yield and every cost is conditioned on it. It\nused to be computed invisibly inside the search, which meant an oxygen-limited\ndesign producing a third of the requested titer generated a purification train\nfor a broth the user had never seen and would not have accepted.\n\nThis endpoint exists so that broth can be put in front of the user and agreed\nbefore the search spends a minute on it.",
    "operationId": "simulate_fermentation_api_simulate_fermentation_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/FermentationPreflightBody"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Simulate Fermentation"
   }
  },
  "/api/simulate-separation": {
   "post": {
    "operationId": "simulate_separation_api_simulate_separation_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SimulateBody"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Simulate Separation"
   }
  },
  "/api/tea/compare": {
   "post": {
    "description": "Full side-by-side TEA comparison of 2-3 processes on one shared basis.\n\nRecomputes each case's detailed TEA at identical operating hours, facility\ntype, labor profile and financial assumptions, then returns per-case line\nitems, per-kg cost breakdowns, deltas against the first case, and the\nwarnings that keep a screening-grade comparison honest (different products\nblock it; scale gaps, purity gaps and within-accuracy-band differences warn).",
    "operationId": "compare_tea_api_tea_compare_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TEACompareBody"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Compare Tea"
   }
  },
  "/api/tea/detailed": {
   "post": {
    "description": "Calculate comprehensive techno-economic analysis (TEA) with LANG factors.\n\nProvides:\n- Capital cost breakdown (LANG factors, direct/indirect costs)\n- Operating cost breakdown (maintenance, labor, utilities, etc.)\n- Production cost metrics (COGS, payback period)\n- Process summary (yield, purity, costs)",
    "operationId": "calculate_detailed_tea_api_tea_detailed_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TEABody"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Calculate Detailed Tea"
   }
  },
  "/api/tea/investor-metrics": {
   "post": {
    "description": "Calculate investor-grade financial metrics: NPV, IRR, EBITDA, discounted payback, ROI.",
    "operationId": "calculate_investor_metrics_api_tea_investor_metrics_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/InvestorMetricsBody"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Calculate Investor Metrics"
   }
  },
  "/api/tea/profiles": {
   "get": {
    "description": "The facility grades and labor profiles the cost model actually knows about.\n\nServed so the pickers in the Economic Analysis panel and the TEA comparison\ndialog are generated from the same catalogue the estimator validates against -\na hardcoded option list drifts the moment a profile is added, and a stale option\nis silently costed as the fallback grade.",
    "operationId": "tea_profiles_api_tea_profiles_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Tea Profiles"
   }
  },
  "/api/tea/scale-analysis": {
   "post": {
    "description": "Calculate TEA metrics across a range of throughput scales.\n\nReturns CAPEX, OPEX, COGS, and payback at each scale point using\nthe six-tenths rule for equipment cost scaling.",
    "operationId": "calculate_scale_analysis_api_tea_scale_analysis_post",
    "requestBody": {
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ScaleAnalysisBody"
       }
      }
     },
     "required": true
    },
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     },
     "422": {
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      },
      "description": "Validation Error"
     }
    },
    "summary": "Calculate Scale Analysis"
   }
  },
  "/api/unit-operations": {
   "get": {
    "operationId": "get_unit_operations_api_unit_operations_get",
    "responses": {
     "200": {
      "content": {
       "application/json": {
        "schema": {}
       }
      },
      "description": "Successful Response"
     }
    },
    "summary": "Get Unit Operations"
   }
  }
 },
 "security": [
  {
   "bearerAuth": []
  }
 ],
 "servers": [
  {
   "description": "production",
   "url": "https://biotechdsp-579272663320.europe-west1.run.app"
  }
 ]
}
