---
title: "Create a planned measurement"
method: POST
path: "/projects/{project_id}/planned_measurements"
tags: ["Planned Measurements"]
---

# Create a planned measurement

`POST /projects/{project_id}/planned_measurements`

## Request

- Base URL: `https://api.ionworks.com`
- URL: `https://api.ionworks.com/projects/{project_id}/planned_measurements`
- Auth: none declared

Create a requested or scheduled planned measurement.

## Path parameters

- `project_id` string, uuid, required

## Request body

- CreatePlannedMeasurement: Request model for creating a planned measurement.
  - `name` string, required
  - `cell_specification_id` string, nullable: Cell specification the requester wants tested (set at request time).
  - `cell_instance_id` string, nullable: Cell instance this future measurement will run on.
  - `channel_id` string, nullable: Channel reserved for this measurement, once scheduled.
  - `set_temperature_c` number, nullable: Temperature this test must run at, in degrees Celsius, held fixed for the whole test. None means ambient: the test runs outside a thermal chamber.
  - `thermal_chamber_id` string, nullable: Chamber this test's channel sits in for the reservation. Recorded per reservation rather than per channel, because channels move between chambers.
  - `protocol_id` string, nullable: Named protocol (experiment_template) this measurement will run. Required on create; a planned measurement must name a protocol.
  - `program_id` string, nullable: Optional catalog program (Formation, Cycling, …) for this test request.
  - `test_setup` object, nullable: Planned physical setup.
  - `planned_start_time` string, date-time, nullable: When setup/run is planned to start.
  - `planned_end_time` string, date-time, nullable: When this planned measurement is expected to release the channel.
  - `estimated_duration_seconds` integer, nullable: Expected run duration before scheduled times exist.
  - `setup_duration_seconds` integer: Operator setup duration before the test starts.
  - `teardown_duration_seconds` integer: Operator teardown duration after the test finishes.
  - `notes` string, nullable: Free-text planning notes.
  - `status` 'requested' | 'scheduled' | 'in_progress' | 'completed' | 'cancelled': Lifecycle state for a planned measurement.

## Response `201`

Successful Response

- PlannedMeasurement: A planned measurement row returned by the API.
  - `name` string, required
  - `cell_specification_id` string, nullable: Cell specification the requester wants tested (set at request time).
  - `cell_instance_id` string, nullable: Cell instance this future measurement will run on.
  - `channel_id` string, nullable: Channel reserved for this measurement, once scheduled.
  - `set_temperature_c` number, nullable: Temperature this test must run at, in degrees Celsius, held fixed for the whole test. None means ambient: the test runs outside a thermal chamber.
  - `thermal_chamber_id` string, nullable: Chamber this test's channel sits in for the reservation. Recorded per reservation rather than per channel, because channels move between chambers.
  - `protocol_id` string, nullable: Named protocol (experiment_template) this measurement will run. Required on create; a planned measurement must name a protocol.
  - `program_id` string, nullable: Optional catalog program (Formation, Cycling, …) for this test request.
  - `test_setup` object, nullable: Planned physical setup.
  - `planned_start_time` string, date-time, nullable: When setup/run is planned to start.
  - `planned_end_time` string, date-time, nullable: When this planned measurement is expected to release the channel.
  - `estimated_duration_seconds` integer, nullable: Expected run duration before scheduled times exist.
  - `setup_duration_seconds` integer: Operator setup duration before the test starts.
  - `teardown_duration_seconds` integer: Operator teardown duration after the test finishes.
  - `notes` string, nullable: Free-text planning notes.
  - `id` string, required
  - `organization_id` string, required
  - `project_id` string, required
  - `status` 'requested' | 'scheduled' | 'in_progress' | 'completed' | 'cancelled', required: Lifecycle state for a planned measurement.
  - `requested_by` string, required
  - `scheduled_by` string, nullable
  - `scheduled_at` string, date-time, nullable
  - `started_measurement_id` string, nullable
  - `cancelled_at` string, date-time, nullable
  - `cancelled_by` string, nullable
  - `cancellation_reason` string, nullable
  - `completed_at` string, date-time, nullable
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `requested_by_email` string, nullable
  - `cancelled_by_email` string, nullable
  - `scheduled_by_email` string, nullable
  - `channel_name` string, nullable
  - `cycler_id` string, nullable
  - `cycler_name` string, nullable
  - `program_name` string, nullable
  - `estimated_duration_status` 'estimating' | 'ready' | 'failed': Lifecycle of a duration estimate computed by a background simulation. Used by both estimates: a planned test's total duration (``planned_measurements.estimated_duration_*``) and a running test's remaining duration (``cell_measurements.estimated_end_time_*``). One enum because the lifecycle is identical -- only what is being estimated differs. ``estimating`` while the job is in flight, ``ready`` once a value came from it, ``failed`` with the reason in the accompanying note. ``None`` means no estimate was ever requested.
  - `estimated_duration_job_id` string, nullable
  - `estimated_duration_simulation_id` string, nullable
  - `simulated_duration_seconds` integer, nullable
  - `estimated_duration_is_exact` boolean, nullable
  - `estimated_duration_margin_pct` number, nullable
  - `actual_duration_seconds` integer, nullable
  - `estimated_duration_note` string, nullable
  - `estimated_duration_calculated_at` string, date-time, nullable

## Other responses

- `422`: Validation Error

## Changes

- **2026-10-02** `af9fdad1dc5b`: 3 info
  - added the optional property `cancellation_reason` to the response with the `201` status
  - added the optional property `cancelled_by` to the response with the `201` status
  - added the optional property `cancelled_by_email` to the response with the `201` status
- **2026-09-30** `ea3d377fec1a`: 1 breaking
  - for the `path` request parameter `project_id`, the format changed from no format to `uuid`

[Operation history](https://skmtc.dev/ionworks/apis/ionworks-api/changes/projects/:project_id/planned_measurements/post.md)

---

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