Skip to main content
A twin is one episode of a robotics dataset rebuilt as an interactable simulation. Kite reconstructs the room from the episode’s camera, builds the objects the robot handles with their real size and physics, checks every stage by measurement, and delivers the scene as a single archive you open in MuJoCo.
Private 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.
You give Kite one thing: the dataset. A twin takes about 100 minutes, so the API is asynchronous — create it, then poll it or get a webhook when it’s done.

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 tells you how long a twin typically takes. It creates nothing and costs nothing.
If something is wrong, the error names it and the field to fix. For example, a camera that doesn’t exist lists the ones that do:

Create a twin

The response is the twin, queued, with 202 Accepted:
The dataset is checked before anything starts, so a twin that can’t work is refused at create, for free, with the same errors as validate.

Request body

Idempotency

Send an Idempotency-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 with GET /v1/twins/:id. Poll every 30 seconds or so, or register a webhook for twin.completed, twin.failed, and twin.canceled and skip polling.
A twin moves through 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, environment, decompose, label, author, refine, assemble, dynamics, and publish.

Download the scene

Once the twin has succeeded, download describes the whole scene as one .tar.gz:
url is a signed link straight to the archive. Fetch it without your API key, check the checksum, and extract:
The folder holds scene.xml — load it with mujoco.MjModel.from_xml_path(...) — and assets/: meshes, textures, and the room as a Gaussian splat. The link expires after an hour; retrieve the twin again for a fresh one. GET /v1/twins/:id/archive does the same in one call: it redirects to a fresh link (curl -L). To see what’s in the archive without downloading it, list its files:

Without writing HTTP

The Kite CLI and Python SDK wrap the whole flow — create, wait, download, verify, and extract:
Both put twins in the folder you give, else in $KITE_TWINS_DIR, else in ./twins, and report absolute paths. Agents get the same flow as the MCP tools kite_twin_validate, kite_twin_create, 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.

The twin object