xPlantAPI

Errors

Every error code the API returns, what it means, and what your integration should do about it.

A failed request answers with a non-2xx status and this body:

{ "ok": false, "data": null, "error": "Missing scope: write:tasks", "code": "FORBIDDEN" }

Branch on code, never on error. Codes are stable; the error text is written for people and may be reworded. Several different failures can share a status (a 403 can be a missing scope, the owner's role, or the wrong kind of credential), so the status alone isn't enough either. As a rule of thumb, 402 is about the plan and 403 is about the role or a missing scope.

Error codes

StatusCodeMeaning
400VALIDATION_ERRORThe request body is not valid JSON. Or: barcode is missing or blank.
401UNAUTHORIZEDThe key is missing, malformed or revoked, or its owner is no longer a member of the workspace.
402DEVICE_LIMIT_REACHEDThe workspace has connected every device its plan includes — retire a device it no longer uses, or contact support to raise the limit — or its plan includes no connected devices at all. error says which. Nothing was registered.
402FEATURE_NOT_INCLUDEDCulture line pricing, which includes sell-through, is not included in your plan. Or: Equipment calibration and maintenance records are not included in your plan. Or: A calibration or maintenance record was sent, and equipment calibration is not included in your plan. Or: Culture line pricing is not included in your plan.
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. Or: The workspace has no paid plan. Connecting devices is included with every paid plan.
402PLAN_LIMIT_REACHEDThe key's owner has created as many explants as their plan allows. Or: The key's owner has created as many plants as their plan allows. error says which limit.
403DEVICE_TOKEN_NOT_ACCEPTEDA device token was sent; this operation needs a workspace API key.
403DEVICE_TOKEN_WRONG_DEVICEA device token was used to write about a device other than its own.
403EXPLANT_WRITE_FORBIDDENThe explant was created by someone else, and the key's owner is not a manager or owner of the lab.
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. Or: The key's owner is below the member role in the workspace. A key never does more than its owner can in xPlant; error names the role needed.
403MEDIA_RECIPE_NOT_OWNERThe key's owner can see the recipe but did not create it. Only a recipe's creator can change it.
403PLANT_WRITE_FORBIDDENThe plant was created by someone else, and the key's owner is not a manager or owner of the lab.
404NOT_FOUNDtarget_id does not name a record of that kind in the key's workspace. Or: No file with this id is attached to a record in the key's workspace. Or: entity_id names no record of that entity_type in the key's workspace. Or: entity_id names no record of that entity_type in the workspace, or parent_id names no comment on it; error says which. Or: product_link_id names no store product in the key's workspace. Or: plant_id names no culture line in the key's workspace. Or: plant_id or explant_id names no plant or explant in the key's workspace; error says which. Or: No record with this id exists in the key's workspace. Or: device_id does not name a device in the workspace. Or: room_id does not name a growing room in the workspace. Nothing was registered. Or: No device with this id is registered in the key's workspace. Or: No token with this id belongs to this device in the key's workspace. Or: The equipment, or a use's subject_id, is not in the key's workspace. Or: The plant or explant named in the scan is not in this workspace. Or: Nothing in this workspace matches the code. Or: No recipe with this id is visible to the key's owner in the workspace: it does not exist there, or it is another member's private recipe. Or: A device_id or room_id in the request is not in the workspace. Nothing was stored. Or: The SOP, or the plant named in plant_id, is not in this workspace. Or: No run with this id exists in the key's workspace, or the run id is not a well-formed id. Or: No SOP with this id exists in the key's workspace, or id is not a well-formed id. Or: The plant or explant is not in this workspace. Or: The plant or explant, or the growing room named in room_id, is not in this workspace. Nothing was changed. Or: No task with this id exists in the key's workspace, or id is not a well-formed id.
409CONFLICTThe task's discussion is locked. Only the task's creator or a lab manager can comment.
409DEVICE_INGEST_DISABLEDA device in the request is paused or retired, so its readings are refused. error names it; nothing was stored.
409DUPLICATE_ENTRYAnother explant in the workspace already uses this external_id, including one that has been deleted. Or: Another plant in the workspace already uses this external_id, including one that has been deleted.
409IDEMPOTENCY_IN_FLIGHTA request with this Idempotency-Key is still being processed; retry after Retry-After seconds.
409SOP_RUN_CLOSEDThe run is complete. A completed run's record is final and takes no more evidence.
409SOP_RUN_NOT_EFFECTIVEThe SOP has no version in force, so there is nothing approved to run. Put a version into force in xPlant, then start the run.
413PAYLOAD_TOO_LARGEThe image is larger than 10 MB.
415UNSUPPORTED_MEDIA_TYPEThe file is not a JPEG, PNG, WebP or GIF image. The type is read from the file itself.
422IMAGE_URL_FETCH_FAILEDimage_url could not be downloaded: it redirected, answered with an error status, took longer than 10 seconds, or its host could not be found. error says which.
422INVALID_CURSORThe cursor is malformed, or came from a different list or different filters. Start again without it.
422VALIDATION_ERRORBoth cursor and offset were sent; use one. Or: target or target_id is missing or not valid. Or: The Idempotency-Key header is malformed. Or: A field failed validation. error names the first one, for example title: title is required. Or: target_id does not name a record of that kind in the key's workspace. Or: image_url is not an https address on the public internet. Or: entity_type or entity_id is missing or malformed. Or: A mentioned user is not an active member of the workspace, or a linked record is not in it. Or: A query parameter is malformed, or from is later than to. error names it. Or: A filter is malformed; error names it. Or: custom_fields names a field your lab has not defined, or gives a field a value of the wrong kind. error names the field. Or: A field failed validation. error names the first one, for example event_type: Invalid enum value. Or: A field failed validation. error names the first one, for example name: String must contain at least 1 character(s). Or: A field failed validation, such as a name longer than 120 characters. error names it. Or: A query parameter is malformed, from is later than to, or offset was sent without kind. error names the parameter. Or: entity is missing or is not plant or explant, or since is not a valid timestamp. Or: plant_id is not a plant in the workspace. Or: custom_fields names a field the lab has not set up, gives a field a value that does not fit it, or totals more than 10,000 bytes. error names the field. Or: A field failed validation, for example a missing barcode or a scanned_at that is not a UTC timestamp. error gives the first problem but does not name the field. Or: status is not one of the recipe statuses. Or: A query parameter is malformed. error names it. Or: A query parameter is malformed: device_id or room_id is not a UUID, since or until is not a timestamp, since is later than until, or limit is not a whole number. error names the parameter, for example device_id: must be a UUID. Or: A reading failed validation. error names the first field, for example readings.0.value: Expected number, received string. Or: A field failed validation, for example sop_id is missing or is not an id. error gives the first problem but does not name the field. Or: A field failed validation — an event_type outside the list, or a recorded_at that is not a UTC timestamp. error gives the first problem but does not name the field. Or: 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. Or: Neither or both of plant_id and explant_id were sent. Or: A field failed validation, or neither or both of plant_id and explant_id were sent. error names the first problem, for example stage: stage is required. Or: A field failed validation, or neither or both of plant_id and explant_id were sent. error names the first problem, for example transfer_cycle: Number must be greater than or equal to 1. Or: custom_fields names a field your lab has not defined, gives a field a value of the wrong kind, or was sent for a plant transfer. error names the field.
429RATE_LIMIT_EXCEEDEDToo many requests for this key, device token or workspace. Wait Retry-After seconds.
5xx…_FAILEDSomething failed on our side; each endpoint names its own code. Retry later; don't branch on the specific code.

What to do about each

CodeRetry?What to do
VALIDATION_ERRORNoFix the request. The error text names the first field that failed, e.g. title: title is required.
UNAUTHORIZEDNoCheck the key. It may be mistyped or revoked, or its creator may have left the workspace.
PAID_PLAN_REQUIREDNoThe workspace's plan doesn't include this call. Teams and Enterprise include the whole API; Hobby and Pro Lab keys can connect devices only; Free has no API access. See Plans and access.
DEVICE_LIMIT_REACHEDNoRegistering this device would go past your plan's device allowance. Retire a device you no longer use, or see plans.
FORBIDDENNoEither the key lacks the scope named in error (use a key that has it), or the owner's role is below what the scope needs (the message names the role; see Plans and access).
DEVICE_TOKEN_NOT_ACCEPTEDNoThis endpoint needs a workspace key. Device tokens only write readings, events and heartbeats.
DEVICE_TOKEN_WRONG_DEVICENoThe token belongs to a different device than the device_id you sent.
NOT_FOUNDNoThe id doesn't exist in this workspace (or belongs to another one; the two look the same on purpose).
IDEMPOTENCY_IN_FLIGHTYesThe first request with this Idempotency-Key is still running. Wait Retry-After seconds (1) and send the same request again.
RATE_LIMIT_EXCEEDEDYesWait the Retry-After seconds, then continue. See Rate limits.
…_FAILED (5xx)Yes, laterSomething went wrong on our side. Retry with backoff; for a write, reuse the same Idempotency-Key. Don't branch on the specific code.

Each endpoint's page lists the errors it can return.

When you contact support about a failed call, include its X-Request-Id response header (err.requestId in the SDK). It identifies exactly that request.

Retrying well

  • Retry only 429, 409 IDEMPOTENCY_IN_FLIGHT, 5xx and network failures. Everything else fails the same way the second time.
  • Honour Retry-After: it's a whole number of seconds.
  • Back off exponentially on 5xx and network errors, and cap the number of attempts.
  • Before retrying a write, make sure a repeat is harmless: send an Idempotency-Key, or for sensor readings an external_id with recorded_at.

The JavaScript SDK does all of this when you pass retry: true to the client.

Edit on GitHub

On this page