curl --request POST \
--url https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload \
--header 'Content-Type: application/json' \
--data '
{
"cell_instance_id": "<string>",
"measurement": {},
"measurement_type": "time_series",
"steps": {},
"filenames": [
"<string>"
]
}
'import requests
url = "https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload"
payload = {
"cell_instance_id": "<string>",
"measurement": {},
"measurement_type": "time_series",
"steps": {},
"filenames": ["<string>"]
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
cell_instance_id: '<string>',
measurement: {},
measurement_type: 'time_series',
steps: {},
filenames: ['<string>']
})
};
fetch('https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'cell_instance_id' => '<string>',
'measurement' => [
],
'measurement_type' => 'time_series',
'steps' => [
],
'filenames' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload"
payload := strings.NewReader("{\n \"cell_instance_id\": \"<string>\",\n \"measurement\": {},\n \"measurement_type\": \"time_series\",\n \"steps\": {},\n \"filenames\": [\n \"<string>\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload")
.header("Content-Type", "application/json")
.body("{\n \"cell_instance_id\": \"<string>\",\n \"measurement\": {},\n \"measurement_type\": \"time_series\",\n \"steps\": {},\n \"filenames\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"cell_instance_id\": \"<string>\",\n \"measurement\": {},\n \"measurement_type\": \"time_series\",\n \"steps\": {},\n \"filenames\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"name": "<string>",
"id": "<string>",
"cell_instance_id": "<string>",
"organization_id": "<string>",
"project_id": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"instance": {
"name": "<string>",
"id": "<string>",
"cell_specification_id": "<string>",
"organization_id": "<string>",
"project_id": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"batch": "<string>",
"date_manufactured": "2023-12-25",
"measured_properties": {},
"notes": "<string>",
"created_by": "<string>",
"created_by_email": "<string>",
"measurement_ids": [
{}
]
},
"steps_created": 123,
"protocol": {},
"start_time": "2023-11-07T05:31:56Z",
"end_time": "2023-11-07T05:31:56Z",
"estimated_end_time": "2023-11-07T05:31:56Z",
"estimated_end_time_note": "<string>",
"estimated_end_time_calculated_at": "2023-11-07T05:31:56Z",
"test_setup": {},
"step_labels_validated": false,
"notes": "<string>",
"measurement_type": "time_series",
"properties": {},
"file_metadata": {},
"channel_id": "<string>",
"protocol_id": "<string>",
"program_id": "<string>",
"created_by": "<string>",
"created_by_email": "<string>",
"processing_status": "ready",
"processing_error": "<string>",
"source_etag": "<string>",
"row_count": 123,
"time_series_bytes": 123,
"estimated_end_time_status": "estimating",
"estimated_end_time_job_id": "<string>",
"extend_claim_id": "<string>",
"extend_in_progress_at": "2023-11-07T05:31:56Z",
"extend_claimed_at": "2023-11-07T05:31:56Z"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Confirm a signed URL upload and finalize the measurement
Confirm a signed URL upload and finalize the measurement.
curl --request POST \
--url https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload \
--header 'Content-Type: application/json' \
--data '
{
"cell_instance_id": "<string>",
"measurement": {},
"measurement_type": "time_series",
"steps": {},
"filenames": [
"<string>"
]
}
'import requests
url = "https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload"
payload = {
"cell_instance_id": "<string>",
"measurement": {},
"measurement_type": "time_series",
"steps": {},
"filenames": ["<string>"]
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
cell_instance_id: '<string>',
measurement: {},
measurement_type: 'time_series',
steps: {},
filenames: ['<string>']
})
};
fetch('https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'cell_instance_id' => '<string>',
'measurement' => [
],
'measurement_type' => 'time_series',
'steps' => [
],
'filenames' => [
'<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload"
payload := strings.NewReader("{\n \"cell_instance_id\": \"<string>\",\n \"measurement\": {},\n \"measurement_type\": \"time_series\",\n \"steps\": {},\n \"filenames\": [\n \"<string>\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload")
.header("Content-Type", "application/json")
.body("{\n \"cell_instance_id\": \"<string>\",\n \"measurement\": {},\n \"measurement_type\": \"time_series\",\n \"steps\": {},\n \"filenames\": [\n \"<string>\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.ionworks.com/cell_measurements/{measurement_id}/confirm-upload")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"cell_instance_id\": \"<string>\",\n \"measurement\": {},\n \"measurement_type\": \"time_series\",\n \"steps\": {},\n \"filenames\": [\n \"<string>\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"name": "<string>",
"id": "<string>",
"cell_instance_id": "<string>",
"organization_id": "<string>",
"project_id": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"instance": {
"name": "<string>",
"id": "<string>",
"cell_specification_id": "<string>",
"organization_id": "<string>",
"project_id": "<string>",
"created_at": "2023-11-07T05:31:56Z",
"updated_at": "2023-11-07T05:31:56Z",
"batch": "<string>",
"date_manufactured": "2023-12-25",
"measured_properties": {},
"notes": "<string>",
"created_by": "<string>",
"created_by_email": "<string>",
"measurement_ids": [
{}
]
},
"steps_created": 123,
"protocol": {},
"start_time": "2023-11-07T05:31:56Z",
"end_time": "2023-11-07T05:31:56Z",
"estimated_end_time": "2023-11-07T05:31:56Z",
"estimated_end_time_note": "<string>",
"estimated_end_time_calculated_at": "2023-11-07T05:31:56Z",
"test_setup": {},
"step_labels_validated": false,
"notes": "<string>",
"measurement_type": "time_series",
"properties": {},
"file_metadata": {},
"channel_id": "<string>",
"protocol_id": "<string>",
"program_id": "<string>",
"created_by": "<string>",
"created_by_email": "<string>",
"processing_status": "ready",
"processing_error": "<string>",
"source_etag": "<string>",
"row_count": 123,
"time_series_bytes": 123,
"estimated_end_time_status": "estimating",
"estimated_end_time_job_id": "<string>",
"extend_claim_id": "<string>",
"extend_in_progress_at": "2023-11-07T05:31:56Z",
"extend_claimed_at": "2023-11-07T05:31:56Z"
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Path Parameters
Body
Request model for confirming a signed URL upload.
Parent cell instance ID
Measurement data with custom fields
Type of measurement being confirmed.
time_series, file, properties Optional steps. Auto-calculated from time series if omitted
Filenames that were uploaded (for file-type measurements). When provided, skips the slow storage listing verification.
Response
Successful Response
Flat response after confirming a signed URL upload.
Measurement fields (id, name, measurement_type, etc.) are at the top level, alongside the parent instance and upload metadata.
1 - 255Organization this measurement belongs to.
Project this measurement belongs to
Model representing a cell instance as returned by the API.
Show child attributes
Show child attributes
Protocol information. Typical keys: 'name' (str), 'ambient_temperature_degc' (float), 'definition' (dict | str — a UCP protocol definition or another protocol format).
ISO 8601 datetime with timezone (UTC preferred) marking when the test started.
ISO 8601 datetime with timezone (UTC preferred) marking when the test finished. Leave null while the test is still running; set it once the measurement is complete. A null end_time means the cell is still considered 'on test' in the Lab view.
ISO 8601 datetime with timezone (UTC preferred) forecasting when a still-running test will finish. Distinct from end_time (the actual finish): this is an estimate. Null when no estimate has been computed yet.
Free-text explanation of how estimated_end_time was derived.
ISO 8601 datetime with timezone marking when estimated_end_time was last computed.
Physical test setup. Typical keys: 'cycler' (model), 'operator', 'lab', 'channel_number' (int).
Set to true only once step-type labels (Rest, CC charge, CV, discharge, etc.) have been manually reviewed. Server-inferred labels should leave this false.
Free-text notes about the cell measurement
What shape of data this measurement holds. Determines which fields are populated and which upload flow applies. See the class docstring.
time_series, file, properties Flat key/value measurements, populated only when measurement_type is 'properties'. Numeric values use the Quantity format. Example: {'thickness': {'value': 0.52, 'unit': 'mm'}, 'DCIR': {'value': 12, 'unit': 'mohm'}}.
Metadata about uploaded files (MIME types, dimensions, etc.)
Optional ID of the channel this measurement ran on.
Optional ID of the experiment template (protocol) this measurement was run against.
Optional catalog program for this measurement (copied from a linked planned measurement when present).
User ID of the user who created this measurement
Email of the user who created this measurement
Lifecycle of server-side step processing. 'ready' once steps are available; 'pending'/'running' while the upload is being processed asynchronously; 'failed' if processing failed (see processing_error).
pending, running, ready, failed, awaiting_extend, extending Reason processing failed, when processing_status is 'failed'. Also carries why an extend did not apply, in which case the status is back to 'ready' and this is advisory only.
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.
estimating, ready, failed Opaque token identifying the in-flight extend that holds this measurement, and the staging prefix its delta is uploaded to. Non-null exactly while processing_status is 'awaiting_extend' or 'extending'.
Deprecated and always null. Use processing_status.
When the in-flight extend claim was taken. Written only by the claim itself, so an unrelated update to the measurement does not age it. Null when no extend holds the measurement.
Was this page helpful?