Beta. Twins are enabled per account. A call from an account that isn’t enabled returns
403 with code twins_not_enabled. Ask your Kite contact to turn them on.Check a dataset first
POST /v1/twins/validate runs every check a create runs — the dataset exists and has camera video, which camera will seed the room, and the robot can drive it — and tells you how long a twin typically takes. It creates nothing and costs nothing.
Create a twin
queued, with 202 Accepted:
Request body
Idempotency
Send anIdempotency-Key header with every create. A retry with the same key and body returns the same twin — even while the first request is still being processed — so a dropped connection never builds two twins. Reusing a key with a different body returns 409 idempotency_key_reused.
One twin builds at a time per account; a second create returns 429 concurrency_limit_exceeded with a Retry-After header.
Track progress
Retrieve the twin withGET /v1/twins/:id. Poll every 30 seconds or so, or register a webhook for twin.completed, twin.failed, and twin.canceled and skip polling.
queued → processing → succeeded. failed and canceled are the other final states, and canceling sits between a cancel and the build actually stopping. Each finished stage reports what it measured about its own output — ingest, author, refine, assemble, dynamics, twin, and publish. twin is the robot calibrated and replayed in the cell, judged against the episode’s video, and repaired, round by round; its measurement is the best round’s score. environment, decompose, and label are reported as skipped: the twin stage builds the room.
Download the scene
Once the twin hassucceeded, download describes the whole scene as one .tar.gz:
url is the API’s archive path. Fetch it with your API key: it redirects to a signed link to the archive, valid for an hour, and curl doesn’t send your key on to it. Check the checksum, and extract:
scene.xml — the robot, the room, and the objects; load it with mujoco.MjModel.from_xml_path(...) — and assets/, its meshes and textures. Beside them, real_twin.mp4 shows the episode next to its twin, and verdict.json holds the judge’s score and the differences it found. If no round could be exported as a whole twin, the archive holds the objects alone, and the twin stage says so. The path doesn’t expire: each fetch redirects to a fresh link.
To see what’s in the archive without downloading it, list its files:
Without writing HTTP
The Kite CLI wraps the whole flow — create, wait, download, verify, and extract:$KITE_TWINS_DIR, else in ./twins, and reports absolute paths. --robot needs kiteml-cli 0.6.0 or later. Agents get the same flow as the MCP tools kite_twin_validate, kite_twin_create (with robot), and kite_twin_status, whose next field is the exact download command.
Cancel and resume
POST /v1/twins/:id/cancel stops a twin that’s building. It’s canceling for a few seconds, until the build has stopped, then canceled.
A failed twin whose error.resumable is true, or a canceled twin, can continue with POST /v1/twins/:id/resume. Every stage it finished is kept, so only the rest runs again.
Errors
A twin that stops part-way reports
status: "failed" with error.code of interrupted, or stage_failed when a stage’s own check didn’t pass. Both are resumable.