{"name":"Genji Crowd API","version":"1","description":"REST API for a procedural historical Chinese crowd generator. Returns per-character mesh part IDs (body/face/hair), spawn positions, animation clips, garment colours, and GLB asset URLs. Designed for Tuanjie/Unity native renderer integration.","spec":"https://www.genjicrowd.com/openapi.json","demo":"https://www.genjicrowd.com/demo.html","authentication":{"type":"Bearer token","header":"Authorization: Bearer <api_key>","note":"Keys are issued from the Genji dashboard. The sample key below is for integration testing only.","sample_key":"YOUR_API_KEY","sample_crowd_id":"cr_sample01"},"endpoints":[{"method":"GET","path":"/v1/crowd/{crowd_id}","description":"Returns the crowd recipe (config), characters[] (all NPCs with body_id/face_id/hair_id, x/z position, rot_y, scale, phase, anim_clip, and colours), shared_parts list, and assets (GLB URLs). crowd_id format: cr_[a-zA-Z0-9]{8}.","auth":true,"errors":{"401":"missing/invalid key","404":"crowd not found"}},{"method":"GET","path":"/v1/projects","description":"Lists the caller's saved crowds (crowd_id, name, collection, timestamps). Unmetered.","auth":true,"errors":{"401":"missing/invalid key"}},{"method":"POST","path":"/v1/projects","description":"Creates and saves a new crowd from a recipe. Body: { name, recipe_json, collection }. Billable, same metering as GET /v1/crowd/{crowd_id}.","auth":true,"errors":{"401":"missing/invalid key","403":"over plan size limit","429":"quota exhausted"}},{"method":"PUT","path":"/v1/projects/{crowd_id}","description":"Updates an existing crowd's recipe/name/collection. Only billed if recipe_json actually changes.","auth":true,"errors":{"401":"missing/invalid key","403":"over plan size limit / not owner","404":"crowd not found"}}],"mcp_server":{"endpoint":"https://www.genjicrowd.com/mcp","transport":"Streamable HTTP (stateless)","description":"Remote MCP server for AI coding assistants (Claude, Cursor, etc) — list/fetch/create crowds as tools inside a chat. Same Bearer API key as every route above. See API.md → \"MCP server\" for the tool list."},"quick_start":["curl -s -H \"Authorization: Bearer YOUR_API_KEY\" https://www.genjicrowd.com/v1/crowd/cr_sample01","Response: { crowd_id, name, recipe, characters[], shared_parts, assets }","Download assets.skeleton_glb once — contains ALL character meshes as SkinnedMesh nodes on a shared skeleton.","For each character: show body_id + face_id + hair_id + shared_parts nodes; hide the rest. Place at (x, 0, z), rotate Y by rot_y radians. Phase-offset animation: startTime = phase × clipDuration.","For crowds of 1000+ characters, use the GPU-instanced rendering path instead — see rendering_paths below."],"rendering_paths":{"per_character_clone":"Default. SkeletonUtils.clone() + AnimationMixer per character. Simple; expect it to fall below interactive framerates somewhere in the 150-300 character range depending on hardware.","gpu_instanced_bone_texture":"For 1000+ characters. One shared baked bone-matrix texture (assets.bone_tex_bin/assets.bone_tex_manifest) sampled per-vertex, rendered via one THREE.InstancedMesh per body/face/hair part (not per character) — typically ≤36 draw calls for the whole crowd regardless of size. See the \"GPU-instanced rendering\" doc section, reference SDK at /sdk/genji-crowd-instanced.js, live demo at /sdk/genji-crowd-instanced-demo.html."},"coordinate_system":{"handedness":"right-handed","up":"Y","position_units":"metres","glb_scene_units":"metres","glb_note":"The GLB scene is in METRES as loaded — the source skeleton is Maya cm, but the export bakes a 0.01 scale into the GLB root node. Do NOT apply any unit conversion (assets.unit_scale is 1.0). Adding a 0.01 scale yourself will make characters 100x too small. character.scale is a per-character SIZE VARIATION (typically 0.95–1.05), not a unit conversion.","rot_y_zero":"character faces +Z","positive_rot_y":"counter-clockwise viewed from above","unity_conversion":"negate z and rot_y"}}