---
title: "Design Geometry"
method: POST
path: "/v1/geometry/design"
tags: ["v1", "geometry"]
---

# Design Geometry

`POST /v1/geometry/design`

## Request

- Base URL: `https://thrustlab.com`
- URL: `https://thrustlab.com/v1/geometry/design`
- Auth: HTTP bearer

Generate an optimal MIL starting blade from design targets; returns 202 + id.

Dispatches the two-stage MIL design (Betz seed -> L-BFGS efficiency polish,) to the concurrency=1 Celery worker. Stream
progress at ``GET /v1/geometry/design/{id}/stream``. Creating a blade is FREE —
this runs OUTSIDE the credits ledger. The owner is stamped into the Redis
payload at dispatch (IDOR) and the job id starts with ``geom_``.

## Request body

- DesignRequest: Generate an optimal MIL starting blade from design targets. Strict (``extra="forbid"``) with numeric bounds on every field. The fields are the Generate-form inputs: blade count, diameter (-> tip radius), hub radius, design RPM (-> Omega), design airspeed (``0`` = static — the kernel is static-valid), a thrust *or* power target, and the per-station airfoil layout. The server computes the optimum design-Cl distribution itself (the constant Cl that maximizes blade efficiency). There are NO polar coefficients (hard contract) — the geometry + Re/M determine the section physics via NeuralFoil.
  - `air_density` number: Air density kg/m^3
  - `airfoil` string
  - `airfoil_layout` AirfoilBreakpoint[], nullable: Per-station airfoil breakpoints. Only the rootmost entry is used — a MIL design flies one section for the whole blade.
    - `airfoil_ref` string, required
    - `r_over_R` number, required
  - `airspeed` number: Design airspeed m/s; 0 = static
  - `design_rpm` number, required: Design RPM (-> Omega)
  - `diameter` number, required: Diameter in inches (-> tip radius R)
  - `hub_radius` number, required: Hub radius as a fraction of the tip radius (0..1)
  - `num_blades` integer: Blade count
  - `section_airfoils` array[]: Optional per-station Kulfan CST weight pairs [[lower_w, upper_w], ...]. Prefer 'airfoil_layout' — that is what the picker emits.
    - array[]
      - number[]
  - `target_mode` 'thrust' | 'power', required: Drive to a thrust OR power target
  - `target_value` number, required: Thrust (gf) or power (W) target — unit swaps with target_mode

## Response `202`

Successful Response

- CalibrateAcceptedResponse: 202 body — the calibration job was queued.
  - `id` string, required
  - `object` string
  - `status` string

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/thrustlab/apis/thrustlab-api.md) · [All operations](https://skmtc.dev/thrustlab/apis/thrustlab-api/llms.txt) · [OpenAPI document](https://skmtc.dev/thrustlab/apis/thrustlab-api/revisions/c19717e52529?raw)
