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

Record transfers and stages

Log subcultures to fresh media and move plants and explants through tissue-culture stages from a script or a scanner.

Scopes: read:transfers, write:transfers (and read:plants / read:explants to look records up). Endpoints: Record a transfer, List transfers, Advance a stage, List stages.

Find the record first

Transfers and stages belong to a plant or an explant. If your own system knows a batch by its own code, look it up with externalId rather than keeping a separate id mapping:

import { XPlantClient } from "@shmaplex/xplant-sdk";

const client = new XPlantClient({ apiKey: process.env.XPLANT_API_KEY });

const explant = await client.explants.findByExternalId("LINE-0412"); // record or null

A scanner can also resolve a label straight to the record.

Record a transfer

A transfer is a subculture onto fresh media. Give exactly one of plant_id or explant_id. The transfer cycle counts up from the record's history unless you set transfer_cycle yourself.

curl -X POST https://app.xplantpro.com/api/v1/transfers \
  -H "Authorization: Bearer $XPLANT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "explant_id": "7c1d9e2a-3b4f-4a5c-8d6e-1f2a3b4c5d6e",
    "from_location": "Shelf A2",
    "to_location": "Shelf B1",
    "notes": "Routine subculture"
  }'

Optional fields: transfer_date, from_location, to_location, transfer_cycle, notes, custom_fields (your lab's own fields, as on plants and explants), and status: completed (the default) for a transfer that's done, or pending for one planned for transfer_date, which counts as done only once someone marks it completed in xPlant.

transfer_date is a calendar day, so send the day itself, such as 2026-09-24. A full timestamp is accepted too, but its day is read in UTC, so 2026-09-26T08:00:00+09:00 lands on 25 September. Transfers come back with transfer_date as a timestamp; take its first ten characters as the day rather than converting it to your time zone, which can shift it by a day.

Advance a stage

One call completes the current stage and makes the new one current. stage is required; give exactly one of plant_id or explant_id.

Send stage as a key from your lab's stage list. Plants and explants have separate lists: an explant might move to multiplication or root_induction, a plant to production. A stage your lab doesn't use for that kind of record is refused with 422 VALIDATION_ERROR naming it, and nothing changes; an older display name is accepted and the response gives you the key to send next time.

A move happens completely or not at all: if it fails you get 500 STAGE_ADVANCE_FAILED, nothing has moved, and retrying with the same Idempotency-Key is safe.

Only the person who created the plant or explant, or a manager, can move its stage. Otherwise the answer is 403 STAGE_WRITE_FORBIDDEN; remember that a key acts with its owner's role.

curl -X POST https://app.xplantpro.com/api/v1/stages \
  -H "Authorization: Bearer $XPLANT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"explant_id": "7c1d9e2a-3b4f-4a5c-8d6e-1f2a3b4c5d6e", "stage": "multiplication"}'

Optional fields: entered_on, room_id, notes.

Read the history

Both lists take one of plant_id or explant_id and return the most recent first. Transfers and stage moves also appear in change history as transfer and stage_change events.

curl "https://app.xplantpro.com/api/v1/transfers?explant_id=$EXPLANT_ID" -H "Authorization: Bearer $XPLANT_API_KEY"
curl "https://app.xplantpro.com/api/v1/stages?plant_id=$PLANT_ID" -H "Authorization: Bearer $XPLANT_API_KEY"

A transfer counter at the bench

A common build is a scanner plus a button: scan the vessel, press "transferred". The scanner resolves the label to a record, and the button posts a transfer for it.

Device tokens can't record transfers, so a station like this needs a workspace key. Give the station its own key holding only read:labels, write:label_scans and write:transfers, so that a lost station exposes as little as possible and can be revoked on its own. See Bench stations and keys.

Edit on GitHub

On this page