Start an SOP run
Starts a run of an SOP: the record that a procedure was carried out, step by step, against the version the lab had in force.
https://app.xplantpro.com/ api/ v1/ sop-runswrite:sop_runsHonours Idempotency-KeyA bench station typically:
- Reads the SOP with Get an SOP and shows the steps of the version in force.
- Starts a run here, naming the batch or line it is for in
batch_code. - Posts evidence against each step as the work happens — a confirmation, a scanned vessel label, a reading from a balance or pH meter — with Record a step event and Record a step measurement.
The run follows the version in force when it starts, and you cannot choose another. Send an Idempotency-Key so a retried start does not open a second run.
The run is recorded as the key's owner, and your lab's training rule applies to them exactly as it does in xPlant. Where the lab requires current training before an SOP is run, a key whose owner has none on this SOP is refused. Where the lab only asks for a warning, the run starts and meta.training_warning says what is missing, so the station can tell the person running it.
See also: Run an SOP from a bench station.
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.
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 |
|---|---|---|---|
sop_id | string (uuid) | Yes | The SOP to run. It must have a version in force. |
batch_code | string | No | Your lab's own identifier for what is being run, such as a batch or line code. Up to 120 characters. |
plant_id | string (uuid) | No | The plant the run is for, when there is one. It must be in this workspace. |
Example
curl -X POST https://app.xplantpro.com/api/v1/sop-runs \
-H "Authorization: Bearer $XPLANT_API_KEY" \
-H "Idempotency-Key: station-3-wk38-start" \
-H "Content-Type: application/json" \
-d '{
"sop_id": "2e4f6a8c-0b1d-4e3f-9a5b-7c9d1e3f5a7b",
"batch_code": "LINE-0412"
}'Response
201 with { "ok": true, "data": … }. data holds the result.
| Field | Type | Required | Description |
|---|---|---|---|
id | string (uuid) | Yes | — |
sopId | string | null (uuid) | Yes | The SOP being run. |
version | integer | null | Yes | The version of the SOP this run follows — the one in force when it started. It never changes afterwards. |
versionId | string | null (uuid) | Yes | That version's id. |
status | string | Yes | in_progress from the moment a run starts. A run ends as completed, failed or cancelled, closed in xPlant or with Complete an SOP run; runs can also be pending, on_hold or archived. |
batchCode | string | null | Yes | Your lab's own identifier for what is being run. |
startedAt | string | null | Yes | — |
completedAt | string | null | Yes | When the run was completed. Empty while it runs, and for a run that ended failed or cancelled. |
progress | number | Yes | How far through its steps the run is, from 0 to 100, as ticked off in xPlant. |
steps | any[] | Yes | The step checklist as ticked off in xPlant, one entry per step, such as { "id": "step-3", "completed": true }. Empty until someone works the run in xPlant. Evidence posted through the API is in events and does not tick steps off here. |
{
"ok": true,
"data": {
"id": "9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d",
"sopId": "2e4f6a8c-0b1d-4e3f-9a5b-7c9d1e3f5a7b",
"version": 2,
"versionId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
"status": "in_progress",
"batchCode": "LINE-0412",
"startedAt": "2026-09-25T08:02:13.000Z",
"completedAt": null,
"progress": 0,
"steps": []
}
}The envelope can also carry meta:
| Field | Type | Required | Description |
|---|---|---|---|
training_warning | object | Yes | Sent when your lab enforces training and the key's owner's training on this SOP needs their attention: it is missing, expired or revoked and the lab warns rather than refuses, or it is current but lapses within 30 days. The run has started either way. Left out otherwise. |
training_warning.qualification | "untrained" | "expired" | "revoked" | "expiring" | Yes | Where the key's owner stands on this SOP: untrained when no training is recorded, expired when it has lapsed, revoked when it was withdrawn, expiring when it is current but lapses within 30 days. |
training_warning.expires_on | string | null | Yes | The day that training lapsed or lapses, as YYYY-MM-DD, for expired and expiring. Null otherwise. |
| 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. |
403 | TRAINING_REQUIRED | Your lab requires current training before an SOP is run, and the key's owner has none on this one: it was never recorded, has expired, or was revoked. Nothing is started. Once their training is recorded in xPlant, start the run again. |
404 | NOT_FOUND | The SOP, or the plant named in plant_id, is not in this workspace. |
409 | IDEMPOTENCY_IN_FLIGHT | A request with this Idempotency-Key is still being processed; retry after Retry-After seconds. |
409 | SOP_RUN_NOT_EFFECTIVE | The SOP has no version in force, so there is nothing approved to run. Put a version into force in xPlant, then start the run. |
422 | VALIDATION_ERROR | The Idempotency-Key header is malformed. |
422 | VALIDATION_ERROR | A field failed validation, for example sop_id is missing or is not an id. 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_RUN_CREATE_FAILED | The run could not be started. Retry with the same Idempotency-Key. |
Branch on code, never on the error text. See Errors.
Update a media recipe
Changes only the fields you send. Only the recipe's creator can change it.
Get an SOP run
One run: the SOP and version it follows, where it stands, and the evidence posted against its steps, oldest first — the trail to read forwards when you need to know what was done, in what order.