xPlantAPI

Idempotency

Send an Idempotency-Key on a write so retrying it after a timeout can't create a duplicate.

A network can fail after the server has done the work but before you get the answer. Retrying then risks doing the work twice: two tasks, two scans, two runs started. An Idempotency-Key header makes the retry safe.

curl -X POST https://app.xplantpro.com/api/v1/tasks \
  -H "Authorization: Bearer $XPLANT_API_KEY" \
  -H "Idempotency-Key: bench-3-0f2a7c91" \
  -H "Content-Type: application/json" \
  -d '{"title": "Check jar 47"}'

A repeat with the same key returns the first response, with the same status and body, and the response header Idempotent-Replay: true. The write happens once.

Which endpoints honour it

The header is honoured on these endpoints, and only these:

Everywhere else the header is ignored. For sensor readings, make retries safe with an external_id and recorded_at on every reading instead: readings are de-duplicated on the device, the external_id and the time.

The rules

Format8–255 characters from A–Z a–z 0–9 . _ : ~ -. A malformed key answers 422 VALIDATION_ERROR rather than being silently ignored.
ScopeYour key plus the endpoint. The same string on two endpoints is two different writes, and two keys never collide.
How long24 hours. After that, the same key starts a fresh write.
Still runningIf the first request hasn't finished, a repeat answers 409 IDEMPOTENCY_IN_FLIGHT with Retry-After: 1. Wait and send it again; don't assume it failed.
Failed writesAren't replayed. If the first attempt failed, the next attempt runs for real, so a passing glitch isn't pinned to your key for a day.
No headerThe write runs unprotected. Idempotency is opt-in.

Choosing keys

Make one key per intent, not per attempt. Every retry of the same logical write sends the same value; a new write sends a new one.

Good keys come from your own data: station-3-run-wk38-start, scanner-3-<scan uuid>, nightly-sync-2026-09-25-task-8812. A random UUID generated once, stored with the pending write and reused on each retry, also works.

The JavaScript SDK takes { idempotencyKey } as the last argument of any write, and with retry: true it generates a key for you and reuses it across its own retries.

Edit on GitHub

On this page