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

# Promote Draft V1

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

## Request

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

## Path parameters

- `public_id` string, required

## Request body

- SimulationCreateBody
  - `airspeed_m_s` number, required
  - `ambient_temp_c` number
  - `battery_charge_pct` number
  - `battery_component_id` string, nullable
  - `battery_esc_wire_resistance_mohm` number
  - `battery_topology` PackTopologyIn
    - `bus_wire_resistance_mohm` number
    - `cross_charge_warning_acknowledged` boolean
    - `root` union, required
      - PackLeafIn
        - `battery_component_id` string, required
        - `branch_wire_resistance_mohm` number
        - `id` string, required
        - `initial_soc_pct` number, nullable
        - `kind` 'leaf'
      - PackSeriesIn
        - `children` union[], required
          - union
            - PackLeafIn
              - …
            - PackSeriesIn (recursive)
            - PackParallelIn
              - …
        - `kind` 'series'
      - PackParallelIn
        - `branches` union[], required
          - union
            - PackLeafIn
              - …
            - PackSeriesIn
              - …
            - PackParallelIn (recursive)
        - `kind` 'parallel'
        - `voltage_match_tolerance_v` number, required
    - `schema` 1
  - `cooling_source` 'static' | 'airspeed' | 'prop_slipstream' | 'forced_air', nullable
  - `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': Launch intent. 'run' dispatches now; 'queue' reserves a credit slot but defers dispatch; 'draft' persists with no credit reservation and no dispatch..
  - `name` string, nullable
  - `project_id` string, nullable: Project to file this run in (raw UUID or proj_<ksuid>). Omit to use the account's default project, which is created if it does not exist.
  - `propeller_model` 'fast' | 'full': Propeller model for every rotor in this simulation. 'full' runs from the measured blade geometry of a database propeller; 'fast' runs from the printed label (diameter, pitch, blade count, family, camber) and accepts a propeller_label in place of a propeller_component_id.
  - `rotor_groups` RotorGroupIn[]
    - `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` RotorIn[], nullable: One entry per rotor. Preferred over rotor_groups, and required for coaxial stacks: set coax_stack_id to the same non-zero value on the rotors that share an axis, give each a distinct coax_position_m (metres, increasing downstream), and set opposite rotation_sense values for a contra-rotating stack. 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'.
  - `vertical_speed_m_s` number

## Response `202`

Successful Response

- SimulationResource
  - `analysis_type` string, required
  - `battery_component_id` string, required
  - `battery_topology` object, nullable
  - `completed_at` string, nullable: When the run reached a terminal state — completed, failed or canceled. Null until then.
  - `created_at` string, nullable: When the run was submitted.
  - `credits_cost` integer, required
  - `dispatched_at` string, nullable: When the run was handed to the worker queue. Null while it is parked and has never been dispatched. NOT the start of execution: a run submitted with launch_intent=run is dispatched at submit, so this can equal created_at while the run waits for a slot.
  - `display_labels` object, nullable
  - `error` object, nullable, required
  - `id` string, required
  - `input_snapshot` object, nullable
  - `inputs` SimulationInputs, 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` 'simulation', required
  - `plot_config_json` object[], nullable, required
  - `progress` number, nullable, required
  - `project_id` string, required
  - `project_name` string, nullable
  - `propeller_model` string
  - `result` object, nullable, required
  - `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 run and the solve began. Null until then. started_at - dispatched_at is the time spent waiting for a slot; completed_at - started_at is the solve itself.
  - `status` 'draft' | 'queued' | 'running' | 'completed' | 'failed' | 'canceled', 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)
