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.
https://app.xplantpro.com/ api/ v1/ sop-runs/ {id}/ steps/ {stepId}/ eventswrite:sop_stepsHonours Idempotency-KeyEvidence is append-only. There is no edit or delete — to correct something, post another event that says so. Send an Idempotency-Key so a scanner retrying on a patchy connection records one event rather than two.
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 id of one of the steps in the version the run follows — the version in force when the run started, whose steps Get an SOP lists in version.steps while it is still in force. Any other stepId is refused and nothing is recorded. |
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 |
|---|---|---|---|
event_type | "confirmed" | "scanned" | "skipped" | "note" | "device_state" | Yes | What happened at the step: confirmed (it was done), scanned (a label or vessel was scanned), skipped, note (a remark for whoever reads the run later) or device_state (what an instrument reported, such as a hood or autoclave cycle). |
payload | object | No | Any detail worth keeping with the event, as a JSON object — the code that was scanned, the note's text, the device's reading. Stored as sent. Default {}. |
recorded_at | string (date-time) | No | When it happened, as an ISO 8601 timestamp in UTC (2026-09-25T08:30:00Z) or with an offset (2026-09-25T10:30:00+02:00). Defaults to when xPlant receives it. |
Example
curl -X POST https://app.xplantpro.com/api/v1/sop-runs/2a4c6e8b-1d3f-4b5a-a7c9-e1f3a5b7c9d1/steps/step-3/events \
-H "Authorization: Bearer $XPLANT_API_KEY" \
-H "Idempotency-Key: station-3-wk38-step3-scan" \
-H "Content-Type: application/json" \
-d '{
"event_type": "scanned",
"payload": {
"code": "LINE-0412-J07"
},
"recorded_at": "2026-09-25T08:30:00Z"
}'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": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
"runId": "9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d",
"stepKey": "step-3",
"eventType": "scanned",
"recordedAt": "2026-09-25T08:30:00+00:00",
"payload": {
"code": "LINE-0412-J07"
}
}
}| Response header | Meaning |
|---|---|
Idempotent-Replay | true when this response is a replay of an earlier request with the same Idempotency-Key. |
X-RateLimit-Limit | The request budget this answer reports on, per minute: your key's or device token's (1,000) or your workspace's (3,000), whichever has fewer requests left. Left out when the budget could not be read. |
X-RateLimit-Remaining | Requests left in that budget this minute, after this one. 0 on a 429. A workspace's other keys spend its budget too, so this can fall faster than your own requests alone would. |
X-RateLimit-Reset | Whole seconds until that budget's minute ends and it is full again. Never more than 60. |
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. |
404 | NOT_FOUND | stepId is not the id of a step in the version the run follows, or the run follows no recorded version of its SOP and so has no steps to record against. Nothing is recorded. |
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 has ended: its status is one of completed, failed, cancelled, archived. An ended 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 — an event_type outside the list, or a recorded_at that is not a timestamp with a time zone. error names the field with the first problem, for example sop_id: Invalid uuid. |
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.
List a run's evidence
Every piece of evidence posted against a run's steps — confirmations, scans, notes, instrument states and measurements — oldest first, a page at a time.
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.