---
title: "Promote Sweep V1"
method: POST
path: "/v1/sweeps/{public_id}/promote"
tags: ["v1", "sweeps"]
---

# Promote Sweep V1

`POST /v1/sweeps/{public_id}/promote`

## Request

- Base URL: `https://thrustlab.com`
- URL: `https://thrustlab.com/v1/sweeps/{public_id}/promote`
- Auth: HTTP bearer

## Path parameters

- `public_id` string, required

## Headers

- `Idempotency-Key` string, required: Unique key (max 255 chars) making this POST safe to retry. Required on all v1 write requests; replays return the cached response.

## Request body

- SweepCreateBody
  - `airspeed_m_s` number, required
  - `ambient_temp_c` number
  - `battery_charge_pct` number
  - `battery_component_id` string, nullable
  - `battery_esc_wire_resistance_mohm` number
  - `cooling_source` 'static' | 'airspeed' | 'prop_slipstream' | 'forced_air', nullable: Cooling source for the battery convection coefficient — static (natural only) | airspeed (free-stream v_inf) | prop_slipstream (prop induced velocity) | forced_air (fixed velocity). Supersedes the fixed cooling-regime h-table; omit it to resolve the value from the deprecated flight_regime..
  - `custom_battery` V1CustomComponentOverride: Per-request component override for custom-mode submissions. Mirrors :class:`app.schemas.simulation.CustomComponentOverride` but accepts a string base_component_id (raw UUID or ``comp_<ksuid>`` prefix) so callers can use whichever identifier form they hold. Resolved to a UUID in the v1 POST handlers before handoff to the service layer.
    - `base_component_id` string, nullable
    - `name` string, required
    - `spec_json` object, required
  - `density_kg_m3` number, required
  - `flight_duration` number, nullable: Flight time (seconds) for the time-bounded steady-state thermal solve. 0 ≤ x ≤ 86400 (24 h). null disables time-bounding (infinite-hover baseline)..
  - `flight_regime` 'static_bench' | 'prop_wash_mild' | 'prop_wash_strong' | 'forced_air': DEPRECATED — use cooling_source instead. Still accepted: when cooling_source is omitted, this value is mapped to the equivalent battery cooling source (prop_wash_mild and prop_wash_strong → prop_slipstream, forced_air → forced_air, static_bench → static). cooling_source wins whenever both are supplied.
  - `forced_air_velocity_m_s` number, nullable: Fixed cooling velocity (m/s) for cooling_source='forced_air' (fan/duct). Ignored for the other cooling sources; defaults to 10 m/s when omitted..
  - `inflow_mode` 'ground' | 'components'
  - `launch_intent` 'run' | 'queue' | 'draft': What to do with the sweep once it is stored. `run` (the default) reserves the credits and hands it to a worker immediately. `queue` reserves the credits but parks the sweep — it stays `queued` with `dispatched_at` null until you dispatch it with POST /v1/simulations/run-queued (every parked run) or POST /v1/simulations/run-selected (named ids; a sweep id is accepted there). `draft` stores it with no credit reservation and no dispatch; promote it later with POST /v1/sweeps/{id}/promote. Dynamic runs have no parked state — they always dispatch.
  - `name` string, nullable
  - `project_id` string, nullable: Project to file this sweep in (raw UUID or proj_<ksuid>). Omit to use the account's default project, which is created if absent.
  - `propeller_model` 'fast' | 'full': Propeller model for every rotor and every point of this sweep. 'full' runs from measured blade geometry; 'fast' runs from the printed label and accepts a propeller_label per rotor.
  - `rotor_groups` SweepRotorGroupIn[]
    - `count` integer
    - `custom_motor` V1CustomComponentOverride: Per-request component override for custom-mode submissions. Mirrors :class:`app.schemas.simulation.CustomComponentOverride` but accepts a string base_component_id (raw UUID or ``comp_<ksuid>`` prefix) so callers can use whichever identifier form they hold. Resolved to a UUID in the v1 POST handlers before handoff to the service layer.
      - `base_component_id` string, nullable
      - `name` string, required
      - `spec_json` object, required
    - `custom_propeller` V1CustomComponentOverride: Per-request component override for custom-mode submissions. Mirrors :class:`app.schemas.simulation.CustomComponentOverride` but accepts a string base_component_id (raw UUID or ``comp_<ksuid>`` prefix) so callers can use whichever identifier form they hold. Resolved to a UUID in the v1 POST handlers before handoff to the service layer.
      - `base_component_id` string, nullable
      - `name` string, required
      - `spec_json` object, required
    - `esc_motor_wire_resistance_mohm` number
    - `esc_pwm_frequency_khz` number: ESC switching frequency (kHz)
    - `esc_resistance_mohm` number
    - `esc_sync_rectification` boolean: Synchronous rectification (comp_pwm); default ON
    - `esc_timing` 'low' | 'medium' | 'high' | 'auto': Six-step commutation advance preset (low 7.5°/medium 15°/high 22.5°/auto duty-scheduled); FOC ignores it
    - `esc_type` 'foc' | 'six_step': ESC commutation type. Selects K_volt, copper_mult, iron_mult, the six-step commutation-advance physics and the throttle->duty map — not a cosmetic flag. Single-sourced (ESC-7): app/constants_esc.py.
    - `label` string, required
    - `motor_component_id` string, nullable
    - `motor_cooling_source` 'cowling' | 'prop_exit_velocity' | 'custom': Per-rotor-group motor cooling mode. cowling = still air; prop_exit_velocity = forced convection from the prop slipstream (default); custom = fixed velocity or direct thermal resistance.
    - `motor_cooling_velocity_m_s` number, nullable: Custom motor cooling convection velocity (m/s); only used when motor_cooling_source=custom.
    - `motor_r_th` number, nullable: Custom direct motor thermal resistance override (K/W); only used when motor_cooling_source=custom.
    - `motor_t_mag` number, nullable: Optional fixed magnet temperature (°C, -60…250); omit to let the model compute it.
    - `motor_t_w` number, nullable: Optional fixed winding temperature (°C, -60…250); omit to let the model compute it.
    - `propeller_component_id` string, nullable
    - `propeller_label` PropellerLabel: The printed size of a propeller — everything the Fast model reads. This is the whole input the Fast model takes for a rotor: no station arrays, no section airfoils, nothing that has to be measured. It is accepted ONLY with ``propeller_model == "fast"``; the Full model needs blade geometry and refuses a label rather than silently switching models.
      - `camber_pct` number: Section camber as a percentage of chord. The default is the reference model's 3.2 % (zero-lift angle -3.5 deg), so a label with no camber given reproduces the published closed-form numbers.
      - `chord_ratio` number, nullable: Chord at 75 % radius divided by diameter. Required when family == 'custom'; rejected otherwise (the family preset is the value).
      - `diameter_m` number, required: Propeller diameter in metres.
      - `family` 'thin_electric' | 'sport' | 'slow_flyer' | 'multirotor' | 'custom', required: Planform family, which sets the chord ratio at 75 % radius (thin_electric 0.065, sport 0.07, slow_flyer 0.10, multirotor 0.11). Use 'custom' to give chord_ratio directly.
      - `max_rpm` integer, nullable
      - `num_blades` integer, required: Number of blades.
      - `pitch_m` number, required: Geometric pitch in metres.
      - `weight_g` number, nullable
    - `throttle_pct` number, required
    - `tilt_deg` number: Ground-mode rotor-axis tilt measured from the horizontal-forward (flight) direction (0° = forward/cruise — the axis lies along the flight path and all airspeed becomes axial inflow; 90° = lift/hover — the axis points up and forward airspeed becomes pure edgewise inflow). Decomposed host-side to V_axial = V_h·cosθ + V_v·sinθ, V_edge = |V_v·cosθ − V_h·sinθ|.
    - `v_axial_m_s` number, nullable: Components-mode signed axial inflow (m/s, up to 300); only with inflow_mode='components'.
    - `v_edge_m_s` number, nullable: Components-mode edgewise inflow magnitude (m/s, ≥0, up to 300); only with inflow_mode='components'.
  - `rotors` SweepRotorIn[], nullable: One entry per rotor. Preferred over rotor_groups, and required for coaxial stacks. sweep.rotor_sweep_mask and sweep.tilt_sweep_mask carry one bool per entry of this list, and component_axes[].slots index into it. Supply either rotors or rotor_groups, not both.
    - `coax_position_m` number: Position along the coaxial stack axis in metres, increasing downstream (in the direction the slipstream travels). The spacing between two rotors is the difference of their positions. Ignored when coax_stack_id is 0.
    - `coax_stack_id` integer: Coaxial stack this rotor belongs to. 0 (default) means the rotor is not stacked and is simulated in isolation. Rotors sharing a non-zero value form one contra-rotating or co-rotating stack.
    - `custom_motor` V1CustomComponentOverride: Per-request component override for custom-mode submissions. Mirrors :class:`app.schemas.simulation.CustomComponentOverride` but accepts a string base_component_id (raw UUID or ``comp_<ksuid>`` prefix) so callers can use whichever identifier form they hold. Resolved to a UUID in the v1 POST handlers before handoff to the service layer.
      - `base_component_id` string, nullable
      - `name` string, required
      - `spec_json` object, required
    - `custom_propeller` V1CustomComponentOverride: Per-request component override for custom-mode submissions. Mirrors :class:`app.schemas.simulation.CustomComponentOverride` but accepts a string base_component_id (raw UUID or ``comp_<ksuid>`` prefix) so callers can use whichever identifier form they hold. Resolved to a UUID in the v1 POST handlers before handoff to the service layer.
      - `base_component_id` string, nullable
      - `name` string, required
      - `spec_json` object, required
    - `esc_motor_wire_resistance_mohm` number
    - `esc_pwm_frequency_khz` number: ESC switching frequency (kHz)
    - `esc_resistance_mohm` number
    - `esc_sync_rectification` boolean: Synchronous rectification (comp_pwm); default ON
    - `esc_timing` 'low' | 'medium' | 'high' | 'auto': Six-step commutation advance preset (low 7.5°/medium 15°/high 22.5°/auto duty-scheduled); FOC ignores it
    - `esc_type` 'foc' | 'six_step': ESC commutation type. Selects K_volt, copper_mult, iron_mult, the six-step commutation-advance physics and the throttle->duty map — not a cosmetic flag. Single-sourced (ESC-7): app/constants_esc.py.
    - `label` string, required
    - `motor_component_id` string, nullable
    - `motor_cooling_source` 'cowling' | 'prop_exit_velocity' | 'custom': Per-rotor-group motor cooling mode. cowling = still air; prop_exit_velocity = forced convection from the prop slipstream (default); custom = fixed velocity or direct thermal resistance.
    - `motor_cooling_velocity_m_s` number, nullable: Custom motor cooling convection velocity (m/s); only used when motor_cooling_source=custom.
    - `motor_r_th` number, nullable: Custom direct motor thermal resistance override (K/W); only used when motor_cooling_source=custom.
    - `motor_t_mag` number, nullable: Optional fixed magnet temperature (°C, -60…250); omit to let the model compute it.
    - `motor_t_w` number, nullable: Optional fixed winding temperature (°C, -60…250); omit to let the model compute it.
    - `propeller_component_id` string, nullable
    - `propeller_label` PropellerLabel: The printed size of a propeller — everything the Fast model reads. This is the whole input the Fast model takes for a rotor: no station arrays, no section airfoils, nothing that has to be measured. It is accepted ONLY with ``propeller_model == "fast"``; the Full model needs blade geometry and refuses a label rather than silently switching models.
      - `camber_pct` number: Section camber as a percentage of chord. The default is the reference model's 3.2 % (zero-lift angle -3.5 deg), so a label with no camber given reproduces the published closed-form numbers.
      - `chord_ratio` number, nullable: Chord at 75 % radius divided by diameter. Required when family == 'custom'; rejected otherwise (the family preset is the value).
      - `diameter_m` number, required: Propeller diameter in metres.
      - `family` 'thin_electric' | 'sport' | 'slow_flyer' | 'multirotor' | 'custom', required: Planform family, which sets the chord ratio at 75 % radius (thin_electric 0.065, sport 0.07, slow_flyer 0.10, multirotor 0.11). Use 'custom' to give chord_ratio directly.
      - `max_rpm` integer, nullable
      - `num_blades` integer, required: Number of blades.
      - `pitch_m` number, required: Geometric pitch in metres.
      - `weight_g` number, nullable
    - `rotation_sense` 1 | -1: Direction of rotation about the downstream axis: 1 or -1. Opposite values in one stack give a contra-rotating pair, which recovers swirl energy and is the usual choice. Only read by coaxial pairing — a single rotor's value does not affect its own performance.
    - `throttle_pct` number, required
    - `tilt_deg` number: Ground-mode rotor-axis tilt measured from the horizontal-forward (flight) direction (0° = forward/cruise — the axis lies along the flight path and all airspeed becomes axial inflow; 90° = lift/hover — the axis points up and forward airspeed becomes pure edgewise inflow). Decomposed host-side to V_axial = V_h·cosθ + V_v·sinθ, V_edge = |V_v·cosθ − V_h·sinθ|.
    - `v_axial_m_s` number, nullable: Components-mode signed axial inflow (m/s, up to 300); only with inflow_mode='components'.
    - `v_edge_m_s` number, nullable: Components-mode edgewise inflow magnitude (m/s, ≥0, up to 300); only with inflow_mode='components'.
  - `sweep` SweepConfigIn, required
    - `airspeed` SweepParamRangeIn: One numeric sweep axis: an inclusive linear range, or an explicit list.
      - `mode` 'range' | 'custom', required: `range` walks `start` to `stop` in `steps` evenly spaced points; `custom` uses the explicit `values` list.
      - `start` number, nullable: First value of a `range` axis (inclusive). Required for `range`.
      - `steps` integer, nullable: How many POINTS the axis has — NOT a step size. 2 to 1000. start=10, stop=100, steps=10 gives 10, 20, 30 ... 100; both endpoints are included and the spacing is (stop-start)/(steps-1). Required for `range`.
      - `stop` number, nullable: Last value of a `range` axis (inclusive). Required for `range`, and must be greater than `start`.
      - `values` number[], nullable: Explicit axis values for `custom` mode, used exactly as given (no sorting, no de-duplication). Required for `custom`.
    - `battery_charge` SweepParamRangeIn: One numeric sweep axis: an inclusive linear range, or an explicit list.
      - `mode` 'range' | 'custom', required: `range` walks `start` to `stop` in `steps` evenly spaced points; `custom` uses the explicit `values` list.
      - `start` number, nullable: First value of a `range` axis (inclusive). Required for `range`.
      - `steps` integer, nullable: How many POINTS the axis has — NOT a step size. 2 to 1000. start=10, stop=100, steps=10 gives 10, 20, 30 ... 100; both endpoints are included and the spacing is (stop-start)/(steps-1). Required for `range`.
      - `stop` number, nullable: Last value of a `range` axis (inclusive). Required for `range`, and must be greater than `start`.
      - `values` number[], nullable: Explicit axis values for `custom` mode, used exactly as given (no sorting, no de-duplication). Required for `custom`.
    - `component_axes` ComponentSweepAxisIn[]
      - `axis` 'motor' | 'propeller' | 'battery', required
      - `component_ids` string[], required: Component IDs (raw UUID or comp_<ksuid>) to sweep over; >=2 (a 1-value axis is not a sweep), at most 10000
      - `slot` integer: Rotor-group index this axis targets (ignored for battery). Back-compat single-slot; prefer `slots`.
      - `slots` integer[], nullable: Rotor-group indices this axis targets. >1 entry → SYNCED (one shared dimension). Omitted → [slot]. Battery must be a single slot.
    - `density` SweepParamRangeIn: One numeric sweep axis: an inclusive linear range, or an explicit list.
      - `mode` 'range' | 'custom', required: `range` walks `start` to `stop` in `steps` evenly spaced points; `custom` uses the explicit `values` list.
      - `start` number, nullable: First value of a `range` axis (inclusive). Required for `range`.
      - `steps` integer, nullable: How many POINTS the axis has — NOT a step size. 2 to 1000. start=10, stop=100, steps=10 gives 10, 20, 30 ... 100; both endpoints are included and the spacing is (stop-start)/(steps-1). Required for `range`.
      - `stop` number, nullable: Last value of a `range` axis (inclusive). Required for `range`, and must be greater than `start`.
      - `values` number[], nullable: Explicit axis values for `custom` mode, used exactly as given (no sorting, no de-duplication). Required for `custom`.
    - `esc_pwm_frequency_khz` SweepParamRangeIn: One numeric sweep axis: an inclusive linear range, or an explicit list.
      - `mode` 'range' | 'custom', required: `range` walks `start` to `stop` in `steps` evenly spaced points; `custom` uses the explicit `values` list.
      - `start` number, nullable: First value of a `range` axis (inclusive). Required for `range`.
      - `steps` integer, nullable: How many POINTS the axis has — NOT a step size. 2 to 1000. start=10, stop=100, steps=10 gives 10, 20, 30 ... 100; both endpoints are included and the spacing is (stop-start)/(steps-1). Required for `range`.
      - `stop` number, nullable: Last value of a `range` axis (inclusive). Required for `range`, and must be greater than `start`.
      - `values` number[], nullable: Explicit axis values for `custom` mode, used exactly as given (no sorting, no de-duplication). Required for `custom`.
    - `esc_timing_values` string[], nullable
    - `label_sweep_mask` boolean[]: One bool per rotor group, same order as `rotor_groups`: true = this group's propeller label takes the point's swept diameter, pitch and blade count. Groups left false keep their own label. Omit or leave empty when no label axis is swept — a label range with an all-false mask is inert and does not count as a swept axis. When non-empty its length must equal `rotor_groups`.
    - `prop_diameter_m` SweepParamRangeIn: One numeric sweep axis: an inclusive linear range, or an explicit list.
      - `mode` 'range' | 'custom', required: `range` walks `start` to `stop` in `steps` evenly spaced points; `custom` uses the explicit `values` list.
      - `start` number, nullable: First value of a `range` axis (inclusive). Required for `range`.
      - `steps` integer, nullable: How many POINTS the axis has — NOT a step size. 2 to 1000. start=10, stop=100, steps=10 gives 10, 20, 30 ... 100; both endpoints are included and the spacing is (stop-start)/(steps-1). Required for `range`.
      - `stop` number, nullable: Last value of a `range` axis (inclusive). Required for `range`, and must be greater than `start`.
      - `values` number[], nullable: Explicit axis values for `custom` mode, used exactly as given (no sorting, no de-duplication). Required for `custom`.
    - `prop_num_blades` SweepParamRangeIn: One numeric sweep axis: an inclusive linear range, or an explicit list.
      - `mode` 'range' | 'custom', required: `range` walks `start` to `stop` in `steps` evenly spaced points; `custom` uses the explicit `values` list.
      - `start` number, nullable: First value of a `range` axis (inclusive). Required for `range`.
      - `steps` integer, nullable: How many POINTS the axis has — NOT a step size. 2 to 1000. start=10, stop=100, steps=10 gives 10, 20, 30 ... 100; both endpoints are included and the spacing is (stop-start)/(steps-1). Required for `range`.
      - `stop` number, nullable: Last value of a `range` axis (inclusive). Required for `range`, and must be greater than `start`.
      - `values` number[], nullable: Explicit axis values for `custom` mode, used exactly as given (no sorting, no de-duplication). Required for `custom`.
    - `prop_pitch_m` SweepParamRangeIn: One numeric sweep axis: an inclusive linear range, or an explicit list.
      - `mode` 'range' | 'custom', required: `range` walks `start` to `stop` in `steps` evenly spaced points; `custom` uses the explicit `values` list.
      - `start` number, nullable: First value of a `range` axis (inclusive). Required for `range`.
      - `steps` integer, nullable: How many POINTS the axis has — NOT a step size. 2 to 1000. start=10, stop=100, steps=10 gives 10, 20, 30 ... 100; both endpoints are included and the spacing is (stop-start)/(steps-1). Required for `range`.
      - `stop` number, nullable: Last value of a `range` axis (inclusive). Required for `range`, and must be greater than `start`.
      - `values` number[], nullable: Explicit axis values for `custom` mode, used exactly as given (no sorting, no de-duplication). Required for `custom`.
    - `rotor_sweep_mask` boolean[], required: One bool per rotor group, same order as `rotor_groups`: true = this group follows the swept throttle axis. INERT when `sweep.throttle` is absent — with no throttle axis there is nothing to follow, and every group keeps its own persisted throttle. Length must equal `rotor_groups` (422 otherwise).
    - `throttle` SweepParamRangeIn: One numeric sweep axis: an inclusive linear range, or an explicit list.
      - `mode` 'range' | 'custom', required: `range` walks `start` to `stop` in `steps` evenly spaced points; `custom` uses the explicit `values` list.
      - `start` number, nullable: First value of a `range` axis (inclusive). Required for `range`.
      - `steps` integer, nullable: How many POINTS the axis has — NOT a step size. 2 to 1000. start=10, stop=100, steps=10 gives 10, 20, 30 ... 100; both endpoints are included and the spacing is (stop-start)/(steps-1). Required for `range`.
      - `stop` number, nullable: Last value of a `range` axis (inclusive). Required for `range`, and must be greater than `start`.
      - `values` number[], nullable: Explicit axis values for `custom` mode, used exactly as given (no sorting, no de-duplication). Required for `custom`.
    - `tilt` SweepParamRangeIn: One numeric sweep axis: an inclusive linear range, or an explicit list.
      - `mode` 'range' | 'custom', required: `range` walks `start` to `stop` in `steps` evenly spaced points; `custom` uses the explicit `values` list.
      - `start` number, nullable: First value of a `range` axis (inclusive). Required for `range`.
      - `steps` integer, nullable: How many POINTS the axis has — NOT a step size. 2 to 1000. start=10, stop=100, steps=10 gives 10, 20, 30 ... 100; both endpoints are included and the spacing is (stop-start)/(steps-1). Required for `range`.
      - `stop` number, nullable: Last value of a `range` axis (inclusive). Required for `range`, and must be greater than `start`.
      - `values` number[], nullable: Explicit axis values for `custom` mode, used exactly as given (no sorting, no de-duplication). Required for `custom`.
    - `tilt_sweep_mask` boolean[]: One bool per rotor group, same order as `rotor_groups`: true = this group sweeps the shared `tilt` angle range as its own grid dimension. Omit or leave empty when no tilt axis is swept. When non-empty its length must equal `rotor_groups` (422 otherwise).
    - `vertical_speed` SweepParamRangeIn: One numeric sweep axis: an inclusive linear range, or an explicit list.
      - `mode` 'range' | 'custom', required: `range` walks `start` to `stop` in `steps` evenly spaced points; `custom` uses the explicit `values` list.
      - `start` number, nullable: First value of a `range` axis (inclusive). Required for `range`.
      - `steps` integer, nullable: How many POINTS the axis has — NOT a step size. 2 to 1000. start=10, stop=100, steps=10 gives 10, 20, 30 ... 100; both endpoints are included and the spacing is (stop-start)/(steps-1). Required for `range`.
      - `stop` number, nullable: Last value of a `range` axis (inclusive). Required for `range`, and must be greater than `start`.
      - `values` number[], nullable: Explicit axis values for `custom` mode, used exactly as given (no sorting, no de-duplication). Required for `custom`.
  - `vertical_speed_m_s` number

## Response `202`

Successful Response

- SweepResource
  - `analysis_type` string, required
  - `battery_component_id` string, required
  - `cancel_requested_at` string, nullable: When a cancel was requested for a RUNNING sweep. The status stays `running` until the worker honours it at its next point, keeping the points already solved. Null when no cancel was requested; a QUEUED sweep cancels immediately instead and never sets this.
  - `completed_at` string, nullable: When the sweep reached a terminal state — completed, failed or canceled. Null until then.
  - `completed_points` integer, required
  - `created_at` string, nullable: When the sweep was submitted.
  - `credits_breakdown` SweepCreditsBreakdown: How this sweep's credit cost was arrived at. A sweep bills one compute unit per evaluation point per physical rotor, so `points * rotors * cu_per_point_per_rotor` is today's price. `total` is what was actually charged: a sweep submitted before the pricing change of 2026-09-03 was billed under a sub-linear curve and reports that lower figure, which is why the two can differ on an old run.
    - `cu_per_point_per_rotor` integer, required: Compute units billed per point per rotor.
    - `points` integer, required: Evaluation points in the grid.
    - `rotors` integer, required: Physical rotors the grid was solved for.
    - `total` integer, required: Credits actually charged for this sweep.
  - `credits_cost` integer, required
  - `dispatched_at` string, nullable: When the sweep was handed to the worker queue. Null while it is parked and has never been dispatched. NOT the start of execution: launch_intent=run dispatches at submit, so this can equal created_at while the sweep waits for a slot.
  - `error` object, nullable, required
  - `id` string, required
  - `input_snapshot` object, nullable
  - `inputs` SweepInputs, required
    - `airspeed_m_s` number, required
    - `battery_charge_pct` number, required
    - `density_kg_m3` number, required
    - `solver` string, required
  - `is_starred` boolean, required
  - `name` string, nullable, required
  - `object` 'sweep', required
  - `plot_config_json` object[], nullable, required
  - `progress` number, nullable, required
  - `project_id` string, required
  - `propeller_model` string
  - `rotor_groups` RotorGroupResource[], required
    - `coax_position_m` number
    - `coax_stack_id` integer
    - `count` integer, required
    - `esc_motor_wire_resistance_mohm` number, required
    - `esc_pwm_frequency_khz` number
    - `esc_resistance_mohm` number, required
    - `esc_sync_rectification` boolean
    - `esc_timing` string
    - `esc_type` string, required
    - `label` string, required
    - `motor_component_id` string, nullable, required
    - `propeller_component_id` string, nullable
    - `propeller_label` object, nullable: The printed size a Fast-model rotor ran from: diameter_m, pitch_m, num_blades, family, chord_ratio, camber_pct, and optionally weight_g and max_rpm. Null on a Full-model rotor.
    - `rotation_sense` integer
    - `throttle_pct` number, required
  - `snapshot_version` integer
  - `solver_engine` string
  - `started_at` string, nullable: When a worker claimed the sweep and solving began. Null until then. started_at - dispatched_at is the wait for a slot; completed_at - started_at is the solve itself.
  - `status` 'draft' | 'queued' | 'running' | 'completed' | 'failed' | 'canceled', required
  - `summary` object, nullable, required
  - `sweep_config` object, nullable, required
  - `total_points` integer, required

## 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)
