Polyfork (https://polyfork.dev) is a low-poly 3D asset store built to be browsed and used by AI agents. This guide is the whole interface: read it, then work straight against the URLs below. Nothing here needs an SDK or an install. CATALOG (public JSON, no auth) GET https://polyfork.dev/api/assets filters: q (search), class (prop|building|vehicle|character|ultra|attachment), free (0|1), kit, max_triangles, min_triangles, sort (latest|tris|price), page, per_page (max 100) max_triangles is the one to reach for on mobile or anything perf-sensitive. Every asset in a LIST carries has_rig (animatable parts), remixable (createAsset knobs), has_night (lights up after dark) and a thumbnail PNG, so you never fetch the detail endpoint just to find out whether something animates or what it looks like. GET https://polyfork.dev/api/assets/{id} full metadata: triangles, size_m (real-world meters), palette (hex colors), style, origin, rigged_parts, detachable_parts, render PNGs, download URLs GET https://polyfork.dev/api/kits and /api/kits/{id} themed sets whose parts share one palette and one scale GET https://polyfork.dev/api a self-describing index of every endpoint, if you would rather discover than trust this file Docs: https://polyfork.dev/llms.txt TOOLS THAT ANSWER, RATHER THAN RETURN ROWS GET https://polyfork.dev/api/assets/{id}/matching what BELONGS IN A SCENE with this asset: same kit first, then shared palette, then compatible real-world scale, spread across classes so you get a house, a tree and a barrel rather than eight houses. Use this instead of comparing hex palettes yourself. GET https://polyfork.dev/api/assets/{id}/variant?want=dark+basalt plain words to a ready createAsset() call for a remixable asset. Check the `confidence` it returns: on anything below high, tell the user what was actually applied instead of presenting it as exact. The `call` is what carries the variant. Read `glb_note` before handing over the `glb`: a colour-only variant is applied at runtime, so that file is still the default paint. https://polyfork.dev/scene?ids=a,b,c composes a scene as a PAGE the user can open and orbit. Hand this over instead of a block of three.js they cannot run without a bundler. Paid assets work too: they render from their public preview. POST https://polyfork.dev/api/demand {"need": "...", "context": "...", "email": "..."} A search reports `coverage` ("none" | "poor" | "ok") whenever you passed a query or a filter. On none or poor, SAY SO and offer to register the request. Do not substitute something unrelated: a pine tree offered for a lunar base is worse than an honest no, and the request tells us what to model next. USING AN ASSET (free assets need no auth) Hotlink or download: ES module https://polyfork.dev/cdn/{id}.mjs import { createAsset } from '...'; scene.add(createAsset()) GLB https://polyfork.dev/cdn/{id}.glb (three.js, Unity, Godot, Blender) REMIXABLE ASSETS (customization knobs, public schema) Some assets are remixable: createAsset(params) accepts typed knobs (colors, choices, toggles, ranges) and curated presets. No-arg calls always reproduce the published asset exactly. GET https://polyfork.dev/cdn/{id}-params.json params: every knob with type, default, label and a plain-language "describe" written for you presets: named knob combinations that map to a look night: the after-dark map, see below To build a variant for your user: read the schema, then call createAsset({ colorway: '...', anyKnob: value }) in their scene. NIGHT LIGHTING has_night on an asset says it lights up after dark. The map itself is the `night` object in that asset's -params.json, NOT in the asset JSON: { "lens": { "color": "#ffcf87", "intensity": 0.6, "describe": "..." } } Each entry names a colour ZONE, found by its albedo. Make those vertices emissive with the given colour, drop your scene lights, and that is the whole night look. `intensity` is optional and defaults to 1. MY API KEY (only for assets my plan covers) Browsing, all metadata and every free asset are public: no key needed. There is no per-asset price: an asset is free, or included in Polyfork Pro (https://polyfork.dev/pricing). For anything beyond the free tier, use the key I gave you in this conversation, or read it from the POLYFORK_KEY environment variable (older setups may call it THREEJS_ASSETS_KEY; use whichever is set). Send it as a header: Authorization: Bearer GET https://polyfork.dev/dl/{id}.glb one asset, GLB GET https://polyfork.dev/dl/{id}.mjs one asset, ES module GET https://polyfork.dev/dl/kit-{kit-id}.zip a whole kit, one file GET https://polyfork.dev/dl/catalog.zip the entire catalogue (Pro/founders) GET https://polyfork.dev/api/me with the same header tells you which account the key belongs to and what it covers. Call it BEFORE promising a download, so a 403 is never the way I find out my plan does not cover it. IF I AM ON PRO: PREFER THE CDN OVER DOWNLOADING /api/me and the MCP who_am_i return a cdn_base like https://polyfork.dev/c/{cdn-token}/ Append {id}.mjs or {id}.glb and use that URL directly in the page. No key, no download step, no file to place, and it stays current when an asset is rebuilt. That token is NOT my API key: it only reads files, it is safe in client-side code, and it can be rotated on its own at https://polyfork.dev/account#cdn. Do not substitute it for the Bearer key anywhere, and do not put the Bearer key in a web page. Never print the key back to me, and never put it in source, a commit or a log. If you need it to survive the session, write it to .env or another gitignored file and read it from there; if the project has no such file, say so and ask before creating one. If I have not given you a key and the variable is missing, ask me for one instead of guessing; keys are created at https://polyfork.dev/account READING THE METADATA size_m is real-world meters and origin is at ground level: models sit on y=0. Every asset is flat-shaded vertex colors on ONE material: one draw call each, no textures to wire up. palette lists the exact hex colors, so you can pick assets that look coherent. rigged_parts maps each animatable part to {type, axis, range}: the pivot is already at the hinge or axle, rotate ranges are degrees, slide ranges are meters. Animate with: asset.getObjectByName(part).rotation[axis] = THREE.MathUtils.degToRad(deg) asset.getObjectByName(part).position[axis] = meters // type: slide detachable_parts can be hidden/removed and the surface behind stays closed. Characters use a standard humanoid skeleton (Mixamo-named bones), so stock clips retarget; sample clips: https://polyfork.dev/anim/clips.json thumbnail and renders are PNG images you can open to SEE an asset before choosing it. BUILDING GOOD SCENES Prefer parts of one kit, or assets with overlapping palettes. Static picks can merge into ONE draw call (all assets share one material): import { mergeAssets } from 'https://polyfork.dev/cdn/merge.mjs'; const { merged, dynamic } = mergeAssets([obj1, obj2, {object: obj3, rig}]); scene.add(merged); dynamic.forEach(d => scene.add(d)); Position assets before merging; rig parts return in `dynamic`, still animatable. Many copies of one model: use THREE.InstancedMesh instead. Never rescale to fake a fit: sizes are real, pick right-sized assets instead. Ground and road tiles snap on the kit grid; use them for terrain. Check the hero render before importing, and read triangles to stay in budget. When I ask for a scene: browse the catalog first, shortlist by hero renders and palette, then import and compose.