{"openapi":"3.1.0","info":{"title":"FormFromLight API","version":"1.2.0","description":"Programmatic access to image-to-3D, text-to-3D, retexture, rigging, and quad-remesh.\n\nRequires an active subscription. Issue keys from the [API key page](\/profile\/api-keys). All endpoints are rate-limited to 120\/min.","contact":{"name":"FormFromLight support","email":"api@formfromlight.com","url":"https:\/\/formfromlight.com\/community"},"license":{"name":"Proprietary"}},"servers":[{"url":"https:\/\/formfromlight.com\/api\/v1","description":"Production"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Account","description":"Identity + balance"},{"name":"Models","description":"List + retrieve previously generated models"},{"name":"Generate","description":"Create new 3D models"},{"name":"Modify","description":"Retexture, rig + retopologise existing meshes"},{"name":"Studio","description":"Animation, VFX and props on rigged models"},{"name":"Kits","description":"Themed, categorised asset bundles"},{"name":"Scenes","description":"Arrangements of models \u00b7 and a kit opened as a walkable world"}],"paths":{"\/whoami":{"get":{"tags":["Account"],"summary":"Authenticated account + balance","responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/AccountInfo"}}}},"401":{"description":"Missing or invalid API key","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/models":{"get":{"tags":["Models"],"summary":"List recent models","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"maximum":100}}],"security":[{"bearerAuth":["models:read"]}],"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#\/components\/schemas\/Model"}}}}}}},"401":{"description":"Missing or invalid API key","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"403":{"description":"Missing required scope","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/models\/{id}":{"get":{"tags":["Models"],"summary":"Retrieve one model with status + download URLs","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"security":[{"bearerAuth":["models:read"]}],"responses":{"200":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ModelDetail"}}}},"404":{"description":"Resource not found","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/generate\/image":{"post":{"tags":["Generate"],"summary":"Generate a 3D model from an image","description":"Provide either `image_url` (public URL we can fetch) or a multipart upload `image`. Token cost depends on tier and quality.","security":[{"bearerAuth":["generate:image"]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenerateImageRequest"}},"multipart\/form-data":{"schema":{"$ref":"#\/components\/schemas\/GenerateImageRequest"}}}},"responses":{"201":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenerationAccepted"}}}},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"422":{"description":"Validation error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/generate\/text":{"post":{"tags":["Generate"],"summary":"Generate a 3D model from a text prompt","security":[{"bearerAuth":["generate:text"]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenerateTextRequest"}}}},"responses":{"201":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/GenerationAccepted"}}}},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"422":{"description":"Validation error","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/models\/{id}\/retexture":{"post":{"tags":["Modify"],"summary":"Re-skin an existing GLB \u00b7 4 tokens","security":[{"bearerAuth":["retexture"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/RetextureRequest"}}}},"responses":{"201":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ChildJobAccepted"}}}},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Source model is not in a completed state","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/models\/{id}\/rig":{"post":{"tags":["Modify"],"summary":"Auto-rig + optional baked animation \u00b7 4 tokens","security":[{"bearerAuth":["rig"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/RigRequest"}}}},"responses":{"201":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ChildJobAccepted"}}}},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Source model is not in a completed state","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/models\/upload":{"post":{"tags":["Models"],"summary":"Upload an existing mesh - free","description":"Ingest a .glb (or self-contained .gltf) you already have. The result behaves like a generated model: rig it, retexture it, retopologise it, animate it in Studio. Costs no tokens - nothing is generated. Externally-referenced buffers and textures are rejected; export with everything embedded.","security":[{"bearerAuth":["models:write"]}],"requestBody":{"required":true,"content":{"multipart\/form-data":{"schema":{"type":"object","required":["mesh"],"properties":{"mesh":{"type":"string","format":"binary","description":".glb or self-contained .gltf, max 256 MB"},"title":{"type":"string","nullable":true},"forward_axis":{"type":"string","enum":["-z","+z"],"default":"-z","description":"Which way the mesh faces. Only matters if you later rig it."}}}}}},"responses":{"201":{"description":"Mesh accepted"},"422":{"description":"Unreadable or unsafe mesh"}}}},"\/models\/{id}\/remesh":{"post":{"tags":["Modify"],"summary":"Retopologise into clean quads - 3 tokens","security":[{"bearerAuth":["remesh"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application\/json":{"schema":{"type":"object","properties":{"face_count":{"type":"integer","minimum":1000,"maximum":200000,"default":30000}}}}}},"responses":{"202":{"description":"OK","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/ChildJobAccepted"}}}},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"Source model is not in a completed state","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/models\/{id}\/animations":{"get":{"tags":["Studio"],"summary":"The motions baked onto a model - free","description":"Each entry is a whole GLB: the mesh, its skeleton and one motion on it. The mesh URLs from GET \/models\/{id} never carry a motion, so this is where a finished animation comes from. Poll until the id you were given reads completed.","security":[{"bearerAuth":["studio:animate"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Animations, newest first"}}},"post":{"tags":["Studio"],"summary":"Animate a rigged model from a text prompt - 3 tokens","description":"Picks the closest motion in the library for your prompt and bakes it onto the model. The model must be rigged - a mesh uploaded with a glTF skin already counts.","security":[{"bearerAuth":["studio:animate"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","minLength":3,"maxLength":500,"example":"walk forward confidently"}}}}}},"responses":{"202":{"description":"Animation queued"},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"403":{"description":"Matched motion needs a higher plan"}}}},"\/models\/{id}\/effects":{"post":{"tags":["Studio"],"summary":"Add a VFX layer - 1-3 tokens by kind","security":[{"bearerAuth":["studio:effects"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["kind","prompt"],"properties":{"kind":{"type":"string","enum":["particles","camera","shader","background"]},"prompt":{"type":"string","minLength":3,"maxLength":500}}}}}},"responses":{"201":{"description":"Effect created"},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/models\/{id}\/props":{"get":{"tags":["Studio"],"summary":"List props attached to a model","security":[{"bearerAuth":["studio:props"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Prop list"}}},"post":{"tags":["Studio"],"summary":"Generate a prop and attach it to a bone - 5 tokens","security":[{"bearerAuth":["studio:props"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","minLength":3,"maxLength":500,"example":"a brass lantern"}}}}}},"responses":{"202":{"description":"Prop generation queued"},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/kits":{"get":{"tags":["Kits"],"summary":"List your kits","security":[{"bearerAuth":["kits"]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":25,"maximum":100}}],"responses":{"200":{"description":"Kit list"}}},"post":{"tags":["Kits"],"summary":"Generate a themed asset bundle - priced per asset","description":"One prompt becomes a categorised set of individually generated models. Text-conditioned over the API; reference-image kits are a multipart flow on the web form.","security":[{"bearerAuth":["kits"]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["prompt","count"],"properties":{"prompt":{"type":"string","minLength":3,"maxLength":500,"example":"stone dungeon props, torch-lit"},"count":{"type":"integer","minimum":4,"maximum":100},"title":{"type":"string","nullable":true},"fidelity":{"type":"string","enum":["standard","ultra"],"default":"standard"},"character_pose":{"type":"string","enum":["tpose","relaxed"],"default":"tpose"},"auto_furnish":{"type":"boolean","default":false}}}}}},"responses":{"202":{"description":"Kit generation queued"},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/kits\/{id}":{"get":{"tags":["Kits"],"summary":"Retrieve a kit and its assets","security":[{"bearerAuth":["kits"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Kit with its assets"},"403":{"description":"Not yours"}}}},"\/kits\/{id}\/world":{"post":{"tags":["Scenes"],"summary":"Open a kit as a world \u00b7 free","description":"Places every building, tree, prop, vehicle and character of a finished kit on a ground plane under the sky its plan chose: buildings along a street, characters on the path, nature and props scattered, walls and towers around the edge. Interiors, furniture and weapons stay out of the layout. Deterministic per kit. Built once; later calls return the same scene unless `rebuild` is true, which lays it out again and keeps the sky.","security":[{"bearerAuth":["scenes"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":false,"content":{"application\/json":{"schema":{"type":"object","properties":{"rebuild":{"type":"boolean","default":false}}}}}},"responses":{"201":{"description":"World built","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/SceneResponse"}}}},"200":{"description":"World already existed","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/SceneResponse"}}}},"403":{"description":"Not yours"},"409":{"description":"The kit is still generating, or has nothing that stands outdoors"}}}},"\/scenes":{"get":{"tags":["Scenes"],"summary":"List your scenes and worlds","security":[{"bearerAuth":["scenes"]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","default":25,"maximum":100}},{"name":"kind","in":"query","schema":{"type":"string","enum":["scene","world"]}},{"name":"kit_id","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"Scene list"}}}},"\/scenes\/{id}":{"get":{"tags":["Scenes"],"summary":"Retrieve a scene with every placement resolved to a mesh URL","description":"Enough to rebuild the scene in any engine: each layout item carries the model, its best GLB (rigged when available), position, Euler rotation in radians, scale and `fit` (the longest dimension the mesh is normalised to before scale). Y-up, metres.","security":[{"bearerAuth":["scenes"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Scene","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/SceneResponse"}}}},"403":{"description":"Not yours"}}},"put":{"tags":["Scenes"],"summary":"Replace the arrangement and\/or environment","description":"Fields left out are kept. `layout` replaces the whole layout when present. `environment` carries the sky preset and ground; a generated sky is never overwritten here. Every model in the layout must be yours.","security":[{"bearerAuth":["scenes"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/SceneUpdate"}}}},"responses":{"200":{"description":"Saved","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/SceneResponse"}}}},"403":{"description":"Not yours"},"422":{"description":"Validation error, or a model in the layout is not yours"}}}},"\/worlds\/from-image":{"post":{"tags":["Scenes"],"summary":"Build a world from one image","description":"Two modes. `playable` (default): every object in the photo is generated as its own textured mesh \u00b7 an image-conditioned kit \u00b7 laid out where the photo had it on a ground plane under a sky painted from the photo, and opened as a world when the models land. Level geometry for Unreal, Unity or Blender. Priced as the kit it is (per object, plus the sky); the response carries the kit; poll GET \/kits\/{id} until world_scene_id is set, then GET \/scenes\/{id}. `backdrop` (25 tokens): a photograph or concept art becomes a place to stand in: first a 360\u00b0 view of it, then layered geometry you can look around and step into, packed as one GLB. Fifteen to twenty minutes. Returns the scene at once with environment.world_status = processing; poll GET \/scenes\/{id} until it is completed, then environment.world_mesh_url holds the geometry (Z-up in its own frame, camera at the origin) and environment.sky_url the panorama. Tokens are returned if it fails. The image URL must be https and publicly fetchable; it is read by the world engine, not by us.","security":[{"bearerAuth":["scenes"]}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["image_url"],"properties":{"image_url":{"type":"string","format":"uri","example":"https:\/\/example.com\/forest.jpg"},"prompt":{"type":"string","maxLength":300,"example":"a misty forest clearing at dawn"},"title":{"type":"string","maxLength":160},"mode":{"type":"string","enum":["playable","backdrop"],"default":"playable"},"count":{"type":"integer","minimum":4,"maximum":24,"default":10,"description":"playable only \u00b7 objects to model from the photo"}}}}}},"responses":{"202":{"description":"World queued \u00b7 a scene (backdrop) or a kit (playable)","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/SceneResponse"}}}},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/scenes\/{id}\/package":{"post":{"tags":["Scenes"],"summary":"Build the engine package \u00b7 free","description":"Finishes the scene in Blender, headless: a collision shell per object (UCX_ for Unreal, a MeshCollider in Unity), LOD1 and LOD2, the terrain as a mesh, exported as FBX with embedded textures, GLB and USD, zipped with the heightmaps, a manifest and a README. Takes a few minutes. Poll GET \/scenes\/{id} until environment.package.status is completed; environment.package.url is the archive. 503 when the server has no Blender.","security":[{"bearerAuth":["scenes"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"202":{"description":"Package queued","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/SceneResponse"}}}},"409":{"description":"A package is already being built"},"422":{"description":"Nothing to package"},"503":{"description":"Blender is not available on this server"}}}},"\/scenes\/{id}\/sky":{"post":{"tags":["Scenes"],"summary":"Generate a sky panorama for the scene \u00b7 3 tokens","description":"A 360\u00b0 equirectangular sky from a prompt, generated in the background and set on the scene when it lands (`environment.sky_status` goes processing \u2192 completed). The tokens are returned if it fails. Poll GET \/scenes\/{id}.","security":[{"bearerAuth":["scenes"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","minLength":3,"maxLength":300,"example":"stormy dusk over a moor, purple clouds"}}}}}},"responses":{"202":{"description":"Sky queued"},"402":{"description":"Insufficient tokens or no active subscription","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"409":{"description":"A sky is already being generated"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"Format: `ffl_\u2026` (issued from \/profile\/api-keys)"}},"schemas":{"AccountInfo":{"type":"object","required":["user_id","name","tokens_remaining"],"properties":{"user_id":{"type":"integer"},"name":{"type":"string"},"tokens_remaining":{"type":"integer"},"subscription":{"type":"string","nullable":true,"enum":["pulse","forge","crown",null]}}},"Model":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string","nullable":true},"status":{"type":"string","enum":["pending","processing","completed","failed"]},"ai_model":{"type":"string","enum":["trellis","tripo-h31"]},"quality":{"type":"string","enum":["draft","standard","high","ultra"]},"style":{"type":"string","enum":["realistic","stylized","toon","anime","pbrmetal"]},"source_kind":{"type":"string","enum":["generation","text","retexture","rig","remesh"]},"parent_model_id":{"type":"integer","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"ModelDetail":{"allOf":[{"$ref":"#\/components\/schemas\/Model"},{"type":"object","properties":{"progress":{"type":"integer","minimum":0,"maximum":100},"prompt":{"type":"string","nullable":true},"vertices":{"type":"integer","nullable":true},"faces":{"type":"integer","nullable":true},"file_size":{"type":"integer","nullable":true},"error_message":{"type":"string","nullable":true},"urls":{"type":"object","nullable":true,"properties":{"glb":{"type":"string","format":"uri","nullable":true},"obj":{"type":"string","format":"uri","nullable":true},"fbx":{"type":"string","format":"uri","nullable":true},"stl":{"type":"string","format":"uri","nullable":true},"usdz":{"type":"string","format":"uri","nullable":true}}}}}]},"GenerateImageRequest":{"type":"object","required":["ai_model"],"properties":{"image_url":{"type":"string","format":"uri","description":"Public image URL we can fetch"},"image":{"type":"string","format":"binary","description":"Multipart upload (alternative to image_url)"},"ai_model":{"type":"string","enum":["trellis","tripo-h31"]},"quality":{"type":"string","enum":["draft","standard","high","ultra"],"default":"standard"},"style":{"type":"string","enum":["realistic","stylized","toon","anime","pbrmetal"],"default":"realistic"},"title":{"type":"string","maxLength":255}}},"GenerateTextRequest":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","minLength":3,"maxLength":500},"quality":{"type":"string","enum":["draft","standard","high","ultra"]},"style":{"type":"string","enum":["realistic","stylized","toon","anime","pbrmetal"]},"title":{"type":"string","maxLength":255}}},"RetextureRequest":{"type":"object","required":["style"],"properties":{"style":{"type":"string","enum":["realistic","stylized","toon","anime","pbrmetal"]},"prompt":{"type":"string","maxLength":300,"description":"Optional refinement prompt"}}},"RigRequest":{"type":"object","properties":{"animation":{"type":"string","nullable":true,"enum":["idle","walk","run","jump","dance","wave","punch","kick",null]}}},"GenerationAccepted":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","example":"pending"},"tokens_charged":{"type":"integer"},"tokens_remaining":{"type":"integer"},"estimated_seconds":{"type":"integer"},"poll_url":{"type":"string","format":"uri"}}},"ChildJobAccepted":{"type":"object","properties":{"id":{"type":"integer"},"parent_model_id":{"type":"integer"},"tokens_charged":{"type":"integer"},"poll_url":{"type":"string","format":"uri"}}},"Error":{"type":"object","properties":{"error":{"type":"string"}}},"Environment":{"type":"object","description":"Sky, ground and generated panorama. Null on a scene that never chose a sky (the composer shows its studio look).","properties":{"preset":{"type":"string","enum":["sky_day","sunset","forest","dojo","arena","neon","void","underwater","space"]},"ground_color":{"type":"string","example":"#6f8f4a"},"ground_size":{"type":"number","description":"Half-extent hint in metres \u00b7 the plane drawn is six times this"},"sky_url":{"type":"string","nullable":true,"description":"Equirectangular PNG once a generated sky finished"},"sky_prompt":{"type":"string","nullable":true},"sky_status":{"type":"string","nullable":true,"enum":["processing","completed","failed",null]},"world_status":{"type":"string","nullable":true,"enum":["processing","completed","failed",null],"description":"Only on a world from an image"},"world_stage":{"type":"string","nullable":true,"enum":["queued","labels","panorama","world","convert",null]},"world_mesh_url":{"type":"string","nullable":true,"description":"The world geometry as one GLB once completed"},"world_source_url":{"type":"string","nullable":true},"world_error":{"type":"string","nullable":true},"ground":{"type":"string","enum":["terrain","water","void"],"description":"What the world stands on \u00b7 land with a shape, land in a sea (terrain.water = true, water at terrain.water_level), or nothing at all (the pieces float at their lift; no terrain)"},"terrain":{"type":"object","nullable":true,"description":"relief, water, water_level, size, res, min\/max height, road, and URLs for the r16 heightmap, the 8-bit PNG and the colour map \u00b7 null in a void world"},"package":{"type":"object","nullable":true,"description":"status (processing | completed | failed), url of the archive when completed, files, objects, warnings, built_at, error"},"packager":{"type":"string","nullable":true,"description":"\"blender\" when the server can build engine packages"}}},"Placement":{"type":"object","required":["model_id","pos","rot","scale"],"properties":{"model_id":{"type":"integer"},"title":{"type":"string","nullable":true},"glb_url":{"type":"string","nullable":true},"rigged":{"type":"boolean"},"pos":{"type":"array","items":{"type":"number"},"minItems":3,"maxItems":3},"rot":{"type":"array","items":{"type":"number"},"minItems":3,"maxItems":3,"description":"Euler XYZ, radians"},"scale":{"type":"number"},"fit":{"type":"number","nullable":true,"description":"Longest dimension in metres the mesh is normalised to before scale (a house 12, a floating island 120); null = composer default (1.6)"},"lift":{"type":"number","nullable":true,"description":"Metres the piece floats above the ground; null or 0 for anything standing on it. Kept when the ground is reshaped"}}},"SceneUpdate":{"type":"object","properties":{"title":{"type":"string","maxLength":160},"layout":{"type":"array","items":{"$ref":"#\/components\/schemas\/Placement"}},"base":{"type":"object","nullable":true,"properties":{"pos":{"type":"array","items":{"type":"number"}},"rot":{"type":"array","items":{"type":"number"}},"scale":{"type":"number"}}},"environment":{"type":"object","nullable":true,"properties":{"preset":{"type":"string","enum":["sky_day","sunset","forest","dojo","arena","neon","void","underwater","space"]},"ground_color":{"type":"string"},"ground_size":{"type":"number"}}}}},"SceneResponse":{"type":"object","properties":{"ok":{"type":"boolean"},"built":{"type":"boolean","description":"Only on \/kits\/{id}\/world \u00b7 whether this call laid the world out"},"scene":{"type":"object","properties":{"id":{"type":"integer"},"title":{"type":"string","nullable":true},"kind":{"type":"string","enum":["scene","world"]},"kit_id":{"type":"integer","nullable":true},"base_model_id":{"type":"integer","nullable":true},"placed":{"type":"integer"},"environment":{"$ref":"#\/components\/schemas\/Environment"},"base":{"type":"object","nullable":true},"layout":{"type":"array","items":{"$ref":"#\/components\/schemas\/Placement"}},"url":{"type":"string","description":"The composer, for a person"}}}}}}}}