Sync tasks
Create, prioritise and complete bench work from your own scheduler, without overriding the order people set by hand.
Scopes: read:tasks, write:tasks. Endpoints: List tasks, Create a task, Get a task, Update a task.
The task endpoints let a lab plan production from its own data (sales demand, pipeline time, success rates) instead of by hand, while people at the bench keep the final say.
Create work
Set the priority, the queue position and the assignee in the same call, so a scheduler places work in one request. Link the task to the plant or explant it's about with entity_type and entity_id (send both or neither).
curl -X POST https://app.xplantpro.com/api/v1/tasks \
-H "Authorization: Bearer $XPLANT_API_KEY" \
-H "Idempotency-Key: sync-2026-09-25-line-0412-transfer" \
-H "Content-Type: application/json" \
-d '{
"title": "Transfer the Alocasia line onto fresh medium",
"category": "transfer",
"priority": "high",
"priority_rank": 1.5,
"due_date": "2026-10-02T09:00:00Z",
"assigned_to": "0d7e4b9a-6f21-4c3e-8a5b-2e9f1c7d4a60",
"genus": "Alocasia"
}'An Idempotency-Key built from your own data (the sync run, the line, the job) means a nightly sync that retries after a timeout can't create the same task twice. See Idempotency.
assigned_to must be an active member of the workspace. Anything else is rejected with 422 VALIDATION_ERROR rather than silently dropped.
Ordering: labels, ranks and the hand-placed rule
Two fields position work:
priorityis the label:low,medium,high,urgent.priority_rankis the actual place in the queue. Lower sorts first, and it's a decimal, so a scheduler can drop a task between two neighbours (between1and2, send1.5) without renumbering everything. Sendnullto clear it and let the label decide.
Anything written through the API counts as automatic ordering, and automatic ordering never overrides a person. If someone has placed a task by hand in xPlant, an ordering change from the API is skipped instead of applied. priority_source on every task tells you who owns its order; manual means a person placed it.
A skip is not an error. A nightly sync that failed on every hand-placed task would be unusable, so the response is still 200 and the rest of your change still applies:
const { task, skipped } = await client.tasks.update(taskId, { priority: "urgent", priority_rank: 0.5 });
if (skipped) {
console.log(`Left "${task.title}" where someone put it by hand.`);
}When a change touches ordering, the response carries meta.priority_write: applied (true or false), reason (manual_override when skipped), the task's priority_source (default, auto or manual), and a plain-language message that's safe to put in a sync log. When a change doesn't touch ordering, meta is left out.
To take a hand-placed task back under automatic control, send release: true with the change. The ordering then applies and the task's order becomes automatic again.
curl -X PATCH "https://app.xplantpro.com/api/v1/tasks/$TASK_ID" \
-H "Authorization: Bearer $XPLANT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"priority": "high", "priority_rank": 1500, "release": true}'Only release a task when a person has asked for it. The rule exists so that the bench can overrule a model.
Move work along the board
workflow_status is the board column: backlog, todo, in_progress, waiting_blocked, review, done. Complete a task by setting it to done:
curl -X PATCH "https://app.xplantpro.com/api/v1/tasks/$TASK_ID" \
-H "Authorization: Bearer $XPLANT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"workflow_status": "done"}'Read the queue
List tasks earliest-due first, optionally by column or assignee:
for await (const task of client.tasks.list({ status: "todo" })) {
console.log(task.due_date, task.priority, task.title);
}Lists leave out entity_link to stay small. Fetch a single task to see which plant or explant it's about.
A nightly sync, in outline
- Pull your own plan (orders, forecasts, pipeline) and work out what should be on the bench.
- Read the open tasks (
status=todo,backlog,in_progress) and match them to your plan. Keep your own mapping from your job ids to xPlant task ids. - Create what's missing, with an
Idempotency-Keyderived from your job id. - Update priorities for the rest. Expect some
skippedresults; those are tasks a person has placed, and that's working as intended. - If you also push demand numbers, send them as demand signals and let xPlant weigh them.