Retrieve a job
Fetch the current state of a single SimReady conversion job. This is what you poll after uploading an existing 3D asset or after Asset Composer supplies a conversion_job_id.
| Method | GET |
| Path | /api/conversions/:id |
The :id is the value returned in data.id from a 3D conversion or simulation, or the conversion_job_id returned by SimReady Asset Composer. A composition ID is not interchangeable with a conversion-job ID.
The endpoint also synchronises the latest status from the upstream pipeline before returning, so you always get a fresh progress, stage, and status.
Examples
Section titled “Examples”curl https://api.rigyd.com/api/conversions/abc123... \ -H "Authorization: Bearer rgyd_live_..."// Simple polling loopasync function waitFor(jobId) { while (true) { const res = await fetch(`https://api.rigyd.com/api/conversions/${jobId}`, { headers: { Authorization: `Bearer ${process.env.RIGYD_API_KEY}` }, }); const { data } = await res.json(); if (data.status === 'completed') return data; if (data.status === 'failed') throw new Error(data.error || 'failed'); await new Promise((r) => setTimeout(r, 3000)); }}import os, time, requests
def wait_for(job_id): headers = {"Authorization": f"Bearer {os.environ['RIGYD_API_KEY']}"} while True: data = requests.get( f"https://api.rigyd.com/api/conversions/{job_id}", headers=headers, ).json()["data"] if data["status"] == "completed": return data if data["status"] == "failed": raise RuntimeError(data.get("error") or "failed") time.sleep(3)Response
Section titled “Response”{ "data": { "id": "abc123...", "physiq_job_id": "phy_...", "status": "completed", "filename": "toolbox.glb", "file_size_bytes": 1245678, "stage": "export", "progress": 100, "error": null, "timing": { "queued_at": "2026-05-06T12:00:01.000Z", "started_at": "2026-05-06T12:00:05.000Z", "completed_at": "2026-05-06T12:01:32.000Z" }, "parameters": { "optimize": true, "target_triangle_count": 50000 }, "report": { /* validation + pipeline metadata */ }, "job_type": "glb_to_simready", "credits_charged": 5, "input": { "model": { "url": "https://assets.rigyd.com/...", "name": "toolbox.glb" }, "images": [], "metadata": null }, "preprocess": { "status": "skipped", "steps": null, "started_at": null, "completed_at": null, "error": null, "input_stats": null, "telemetry": null, "intermediate_glb": null }, "output": { "model": { "url": "https://assets.rigyd.com/.../toolbox.usd", "name": "toolbox.usd", "size": 982341 }, "textures": [ { "url": "https://assets.rigyd.com/.../diffuse.png", "name": "diffuse.png" } ], "usd_package": { "url": "...", "name": "toolbox_usd.zip", "size": 1048576 }, "mjcf_package": { "url": "...", "name": "toolbox-mjcf.zip", "size": 1234 }, "sim_video": null, "sim_gif": null, "sim_log": null }, "source_job": null, "simulations": [], "createdAt": "2026-05-06T12:00:00.000Z", "updatedAt": "2026-05-06T12:01:32.000Z" }}output.usd_package is present for composed SimReady assets whose root USD
references layers under usd/payloads/. The ZIP preserves those relative paths,
textures, and any bundled MDL modules. Older flat-USD jobs return null and remain
available through output.model and output.textures.
See Job lifecycle for the full status enum, the meaning of each field, and how preprocess relates to non-GLB inputs.
Reading the SimReady verdict
Section titled “Reading the SimReady verdict”report.validation carries the conformance result. It never affects status — a completed job is downloadable whatever the verdict — so this is the only place to look:
const v = job.report.validation;
v.kind; // "simready-conformance"v.targeted_profiles; // the profiles Rigyd intends to satisfyv.profiles_passing; // ["Prop-Robotics-Neutral", // "Prop-Robotics-Physx", // "Prop-Robotics-Isaac"] — badge thisv.profiles['Prop-Robotics-Neutral'].status; // "PASS" | "FAIL" | "INCOMPLETE"v.spec_release; // "2026.06.0" — the release scored againstv.counts; // { total, passed, errors, warnings, not_applicable, ... }Each profile also includes version, targeted, requirements, failing[], unchecked[], optional_unmet[], and per-profile counts. Each check includes both status (the actionable result for Rigyd’s targets) and measured_status (the raw finding); an N/A may include na_reason as not-in-profile, not-applicable-to-asset, or planned.
Do not badge overall: it can be WARN because of an advisory finding while every targeted profile is PASS. Badge profiles_passing or one profile’s status.
report.pipeline.physics alongside it carries the measured mass, inertia, friction and restitution. Full detail in SimReady Validation.
Reading the simulation evidence
Section titled “Reading the simulation evidence”Before delivery, the MJCF is loaded into MuJoCo and stepped. Like the SimReady verdict, the result never fails a job. It is evidence that travels with the asset, and issues[] lists what to look at:
const sim = job.report.checks.simulation;
sim.fixed_base; // true: the asset is anchored, so it is held rather than droppedsim.settled; // a free asset came to rest after the dropsim.rest_penetration_m; // deepest resting contactsim.joint_drift; // fixed base: [{ joint, drift_deg | drift_mm }] under gravity alonesim.contacts; // [{ bodies, depth_m, moving_into_static }] at restsim.issues; // [] when nothing needs attentionComposition conversions (authored_to_simready) also push every joint toward each end of its declared range. The push starts at what the joint itself declares resisting, then tries 4× and 16×:
const sweep = job.report.checks.joint_sweep;
sweep.overall; // "PASS" | "WARN" | "FAIL" | "N/A" (no joints)sweep.joints[0].name; // same name as report.joints[].namesweep.joints[0].travel_frac; // fraction of the declared range actually reachedsweep.joints[0].status; // "PASS" at 1×, "WARNING" only at 4×/16× (stiff), // "ERROR" never (blocked)sweep.joints[0].ends; // per end: reached_frac, drive (N·m or N), drive_multipleFor composition conversions, each entry of report.bodies[] also says how its collision was built:
const c = job.report.bodies[0].collision;
c.method; // "surface" | "surface_per_part" | "remesh" | "boxes_only"c.overshoot_m; // how far the collision reaches past the body's own meshc.support_boxes; // flat support surfaces shipped as exact boxes: [{ part, top_z, center, half_extents }]method: "remesh" is the fallback, and it is the one method whose hulls can stand proud of the surface. overshoot_by_face_m shows by how much, per face, in the order [-x, -y, -z, +x, +y, +z].
Reports written before these checks existed carry none of these fields. Treat an absent field as not measured, not as a pass.
When the job is done
Section titled “When the job is done”Continue to Download result.