Record a step measurement
Records a numeric reading against one step of a run — the pH of a medium, the mass of an ingredient, a temperature — with its unit, as the bench station running the SOP reads it from a balance, pH meter or probe.
https://app.xplantpro.com/ api/ v1/ sop-runs/ {id}/ steps/ {stepId}/ measurementswrite:sop_stepsHonours Idempotency-KeyThe reading joins the same trail as the step events, with eventType measured, so a run reads in one order. The unit is required: a number without one is not a record anyone can rely on later. Send an Idempotency-Key so a retried post does not record the reading twice.
Send an Idempotency-Key header to make retries safe: a repeat with the same key within 24 hours returns the first response instead of writing twice. See Idempotency.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | The run's id. |
stepId | string | Yes | The step's id in the version the run follows, as Get an SOP lists it in version.steps. It is recorded as sent and not checked against the procedure. |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | No | Any unique string you choose per logical write. A retry carrying the same key within 24 hours returns the first result instead of writing again. Scoped to your API key and this operation. 8–255 characters. Must match ^[A-Za-z0-9._:~-]+$. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
metric | string | Yes | What was measured, in your own words — temperature, ph, mass. 1–60 characters. |
value | number | Yes | The reading. |
unit | string | Yes | The unit the reading is in, such as C, pH or g. Required. 1–20 characters. |
recorded_at | string (date-time) | No | When it happened, in UTC with a trailing Z, such as 2026-09-25T08:30:00Z. A timestamp with an offset is refused. Defaults to when xPlant receives it. |
notes | string | No | Anything the reading needs beside it. Up to 500 characters. |
Example
curl -X POST https://app.xplantpro.com/api/v1/sop-runs/2a4c6e8b-1d3f-4b5a-a7c9-e1f3a5b7c9d1/steps/step-3/measurements \
-H "Authorization: Bearer $XPLANT_API_KEY" \
-H "Idempotency-Key: station-3-wk38-step3-ph" \
-H "Content-Type: application/json" \
-d '{
"metric": "ph",
"value": 5.7,
"unit": "pH",
"recorded_at": "2026-09-25T08:21:40Z",
"notes": "Initiation medium before pouring."
}'Response
201 with { "ok": true, "data": … }. data holds the result.
| Field | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | — |
runId | string (uuid) | Yes | The run the evidence belongs to. |
stepKey | string | Yes | The step it was recorded against — the stepId it was posted to. |
eventType | string | Yes | confirmed, scanned, skipped, note or device_state for a step event; measured for a measurement. |
recordedAt | string | Yes | When it happened: the recorded_at that was sent, or when xPlant received it. |
payload | object | Yes | The detail sent with the event. For a measurement: metric, value, unit, and notes when there were any. |
{
"ok": true,
"data": {
"id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
"runId": "9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d",
"stepKey": "step-2",
"eventType": "measured",
"recordedAt": "2026-09-25T08:21:40+00:00",
"payload": {
"metric": "ph",
"value": 5.7,
"unit": "pH",
"notes": "Initiation medium before pouring."
}
}
}| Response header | Meaning |
|---|---|
Idempotent-Replay | true when this response is a replay of an earlier request with the same Idempotency-Key. |
X-Request-Id | Identifies this request. Include it when you contact support. |
Errors
| Status | Code | When |
|---|---|---|
400 | VALIDATION_ERROR | The request body is not valid JSON. |
401 | UNAUTHORIZED | The key is missing, malformed or revoked, or its owner is no longer a member of the workspace. |
402 | PAID_PLAN_REQUIRED | The workspace's plan does not include the API. The full API is included with xPlant+ Teams and Enterprise; on Hobby and Pro Lab, keys can connect devices only. |
403 | FORBIDDEN | The key lacks a scope this operation requires, or its owner's current role in the workspace cannot use it — a key never does more than its owner can in xPlant. error names the scope, and for a role, the role it needs. |
403 | DEVICE_TOKEN_NOT_ACCEPTED | A device token was sent; this operation needs a workspace API key. |
404 | NOT_FOUND | No run with this id exists in the key's workspace, or the run id is not a well-formed id. |
409 | IDEMPOTENCY_IN_FLIGHT | A request with this Idempotency-Key is still being processed; retry after Retry-After seconds. |
409 | SOP_RUN_CLOSED | The run is complete. A completed run's record is final and takes no more evidence. |
422 | VALIDATION_ERROR | The Idempotency-Key header is malformed. |
422 | VALIDATION_ERROR | A field failed validation — a missing unit, a value that is not a number, or a recorded_at that is not a UTC timestamp. error gives the first problem but does not name the field. |
429 | RATE_LIMIT_EXCEEDED | Too many requests for this key, device token or workspace. Wait Retry-After seconds. |
500 | SOP_STEP_EVENT_CREATE_FAILED | The evidence could not be saved. Retry with the same Idempotency-Key. |
Branch on code, never on the error text. See Errors.
Record step evidence
Records what happened at one step of a run: someone confirmed it, scanned a label or vessel, skipped it, or left a note, or an instrument reported its state.
List SOPs
The workspace's standard operating procedures, most recently updated first — drafts and archived ones included, with status saying which. Summaries only: fetch one SOP to read the steps of the version in force.