@shmaplex/xplant-sdk 0.7.0 is out: what's new →
xPlantAPI
API referenceSOPs and runs

Complete an SOP run

Ends a run: completed when the procedure was carried out, failed when it was carried out and did not work, cancelled when it was stopped before the end. Afterwards the run reads exactly as one closed in xPlant: its status is the outcome, and completedAt is the time of this call for completed and empty otherwise. A run marked failed raises the same deviation alert it raises in xPlant.

POST/api/v1/sop-runs/{id}/complete
Scope write:sop_runsHonours Idempotency-Key

A run is closed by the person who started it, or by a manager or above in the lab, as in xPlant: the key's owner must be one of them. Once a run has ended it takes no more evidence and cannot be closed again.

Send an Idempotency-Key so a station retrying on a patchy connection gets the first answer back rather than a refusal. The key is matched together with the run and the body: the same key sent with a different outcome, different notes or for another run is not a retry, and is answered on its own — on a run that has already ended, with 409 SOP_RUN_CLOSED.

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.

Path parameters

NameTypeRequiredDescription
idstring (uuid)YesThe run's id.

Headers

NameTypeRequiredDescription
Idempotency-KeystringNoAny 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

FieldTypeRequiredDescription
outcome"completed" | "failed" | "cancelled"YesHow the run ended: completed when the procedure was carried out, failed when it was carried out and did not work, cancelled when it was stopped before the end.
notesstringNoA closing remark, such as why a run was cancelled. It is added to the run's notes in xPlant, after anything already there; nothing already written is replaced. Up to 1000 characters.

Example

curl -X POST https://app.xplantpro.com/api/v1/sop-runs/2a4c6e8b-1d3f-4b5a-a7c9-e1f3a5b7c9d1/complete \
  -H "Authorization: Bearer $XPLANT_API_KEY" \
  -H "Idempotency-Key: station-3-wk38-complete" \
  -H "Content-Type: application/json" \
  -d '{
    "outcome": "completed",
    "notes": "All vessels labelled and moved to shelf 3."
  }'

Response

200 with { "ok": true, "data": … }. data holds the result.

FieldTypeRequiredDescription
idstring (uuid)Yes—
sopIdstring | null (uuid)YesThe SOP being run.
versioninteger | nullYesThe version of the SOP this run follows — the one in force when it started. It never changes afterwards.
versionIdstring | null (uuid)YesThat version's id.
statusstringYesin_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.
batchCodestring | nullYesYour lab's own identifier for what is being run.
startedAtstring | nullYes—
completedAtstring | nullYesWhen the run was completed. Empty while it runs, and for a run that ended failed or cancelled.
progressnumberYesHow far through its steps the run is, from 0 to 100, as ticked off in xPlant.
stepsany[]YesThe 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.
Response
{
  "ok": true,
  "data": {
    "id": "9a8b7c6d-5e4f-4a3b-9c2d-1e0f9a8b7c6d",
    "sopId": "2e4f6a8c-0b1d-4e3f-9a5b-7c9d1e3f5a7b",
    "version": 2,
    "versionId": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
    "status": "completed",
    "batchCode": "LINE-0412",
    "startedAt": "2026-09-25T08:02:13.000Z",
    "completedAt": "2026-09-25T09:14:02.000Z",
    "progress": 0,
    "steps": []
  }
}
Response headerMeaning
Idempotent-Replaytrue when this response is a replay of an earlier request with the same Idempotency-Key.
X-RateLimit-LimitThe 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-RemainingRequests 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-ResetWhole seconds until that budget's minute ends and it is full again. Never more than 60.
X-Request-IdIdentifies this request. Include it when you contact support.

Errors

StatusCodeWhen
400VALIDATION_ERRORThe request body is not valid JSON.
401UNAUTHORIZEDThe key is missing, malformed or revoked, or its owner is no longer a member of the workspace.
402PAID_PLAN_REQUIREDThe 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.
403FORBIDDENThe 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.
403DEVICE_TOKEN_NOT_ACCEPTEDA device token was sent; this operation needs a workspace API key.
404NOT_FOUNDNo run with this id exists in the key's workspace, the run id is not a well-formed id, or the key's owner may not close this run because they neither started it nor manage the lab. All three are answered alike. Nothing changes.
409IDEMPOTENCY_IN_FLIGHTA request with this Idempotency-Key is still being processed; retry after Retry-After seconds.
409SOP_RUN_CLOSEDThe run has already ended: its status is one of completed, failed, cancelled, archived. Nothing changes. Get the run to see how it ended.
422VALIDATION_ERRORThe Idempotency-Key header is malformed.
422VALIDATION_ERRORA field failed validation: outcome is missing or is not completed, failed or cancelled, or notes is longer than 1,000 characters. error names the field with the first problem, for example outcome: Required.
429RATE_LIMIT_EXCEEDEDToo many requests for this key, device token or workspace. Wait Retry-After seconds.
500SOP_RUN_UPDATE_FAILEDThe run could not be closed. Retry with the same Idempotency-Key.

Branch on code, never on the error text. See Errors.

On this page