---
title: "Create Public Simulation"
method: POST
path: "/v1/public/simulate"
tags: ["v1", "public"]
---

# Create Public Simulation

`POST /v1/public/simulate`

## Request

- Base URL: `https://thrustlab.com`
- URL: `https://thrustlab.com/v1/public/simulate`
- Auth: none required

Run a simulation with no account, and return its token, link and result.

Synchronous: the Fast propeller model answers in milliseconds, so there is
no job to poll and no status to render. The response is
``{token, url, public_simulation}``, where ``public_simulation`` is the same
payload ``GET /v1/public/share/{token}`` returns for a saved run.

Refusals a caller can act on: 400 for an unknown component, 403 for a
Pro-only feature, 422 for an over-bound sweep or an unrunnable propeller,
429 with ``Retry-After`` for too many runs from one address, and 503
``public_simulate_busy`` with ``Retry-After`` when every free solver slot is
taken.

## Request body

- PublicSimulateBody: ``SimulationCreateBody`` minus ``project_id``/``launch_intent``, plus ``sweep``. The two inherited account fields are redeclared as null-only rather than deleted: pydantic cannot remove an inherited field, and neutralising them is also the honest wire contract. Sending ``project_id`` is a 422, not a silent drop — an anonymous caller who names a project has misunderstood the endpoint, and answering as though the field had been honoured would be worse than refusing. ``propeller_model`` is inherited and accepted, but the service FORCES it to ``"fast"``: the Full model reads measured blade geometry out of the catalog and is the metered product. A body that asks for ``"full"`` here is not refused — it is answered on the Fast model, and the answer says so (``propeller_model`` and the core's own ``aero_model`` both ride the response), because refusing would strand the "refine with the Full model" link that a signed-out visitor follows.
  - `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': Always 'run'. Draft and queue persist against an account, which an anonymous run does not have.
  - `name` string, nullable
  - `project_id` unknown
  - `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'.
  - `sweep` SweepConfigIn
    - `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 `200`

Successful Response

- PublicSimulateResponse: What ``POST /v1/public/simulate`` answers with.
  - `object` 'public_run'
  - `public_simulation` PublicRunView, required: The public projection of an anonymous run — the payload /s/<token> renders. OPEN on purpose: see the module docstring. The body is the same shape ``GET /v1/public/share/{token}`` returns for a saved run, and it differs between a single point (``result``) and a sweep (``points``, ``sweep``, ``headline_result``).
    - `object` 'public_run'
    - `token` string, required: The run's opaque token (run_<ksuid>).
    - `url` string, required: Path the run is readable at (/s/<token>).
  - `token` string, required: Opaque run token. Claim it with POST /v1/runs/{token}/claim to file the run in an account.
  - `url` string, required: Shareable path for the run (/s/<token>).

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