All animation goes through one endpoint, POST /mascots/{id}/animations, with four body shapes: a preset, a prompt, a reference_upload_id, or a hand-authored clip. Whatever you create is saved on the mascot under its name and travels in the player bundle.
Presets#
15 motions, fitted to each rig without a model call. They answer at once with 201, cost nothing, and work on every character, whatever its shape. Presets are always usable by id (in exports, embeds, canvases) even if you never save them; saving one lets you give it your own intensity, duration and name.
| preset | Default length | Motion |
|---|---|---|
idle | 4s | Breathing, blinking, a little life |
wave | 2s | Friendly hello |
nod | 1.2s | Yes! |
shake | 1.2s | Nope |
bounce | 1s | Springy squash and stretch |
celebrate | 1.6s | Arms up, big joy |
jump | 1.4s | A big hop |
talk | 2s | Chatty mouth for assistants |
sad | 3s | Droopy and blue |
surprised | 1.6s | Whoa! |
dance | 2s | Groove on the beat |
think | 3s | Hmm… pondering |
working | 1.2s | Busy, focused hustle |
listen | 2.4s | Attentive, leaning in |
sleep | 4s | Snoozing |
curl -X POST https://lumorig.com/api/v1/mascots/msc_…/animations -H "Authorization: Bearer $LUMORIG_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "preset": "celebrate", "intensity": 1.4, "duration": 2, "name": "big-win" }'
# 201 → { "animation": { "id": "anm_…", "name": "big-win", "source": "preset", "preset": "celebrate", "duration": 2, "loop": true, … } }| Name | Type | Description |
|---|---|---|
presetrequired | string | One of the ids above. GET /presets lists them without a key. |
intensity | number | 0.25–2. About 0.5 is subtle, 1 normal, 1.5 big. |
duration | number | Seconds, 0.3–20. Defaults to the preset's length. |
name | string | Max 60 characters. Defaults to the preset's label (e.g. Celebrate). |
Motions from words#
Describe anything else and the Animator agent choreographs it for this rig: keyframes with anticipation and overshoot, expression changes, and spring follow-through on dangling parts. It costs 20 credits, runs as a job, and usually finishes in under a minute.
curl -X POST https://lumorig.com/api/v1/mascots/msc_…/animations -H "Authorization: Bearer $LUMORIG_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "prompt": "hops twice, then does a tiny bow", "duration": 2.5, "name": "bow" }'
# 202 → { "job": { "id": "job_…", "type": "animate", "status": "queued", … } }| Name | Type | Description |
|---|---|---|
promptrequired | string | The motion, 3–1,000 characters. Describe beats and timing, not camera moves. |
duration | number | Seconds, 0.3–20. Omit it to let the Animator choose. |
name | string | Max 60 characters. Defaults to the name the Animator gives the clip. |
max_cost_usd | number | Stop the job if its model spend passes this. |
The finished job's result has animation_id, the animation itself and a one-line summary. While it works, the event stream carries draft_clip events with each draft clip.
Motions from a reference video#
Film someone (or something) doing the motion, upload the clip, and the Animator watches frames sampled from it and performs the same motion on your rig. Same price as a motion from words: 20 credits.
# 1. upload the reference (MP4, MOV or WebM, up to 15 MB)
curl -X POST https://lumorig.com/api/v1/uploads -H "Authorization: Bearer $LUMORIG_API_KEY" -F "file=@./wave.mp4"
# 201 → { "id": "file_…", "kind": "video", "mime": "video/mp4", … }
# 2. perform it
curl -X POST https://lumorig.com/api/v1/mascots/msc_…/animations -H "Authorization: Bearer $LUMORIG_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "reference_upload_id": "file_…", "prompt": "only the right arm; keep the body still" }'| Name | Type | Description |
|---|---|---|
reference_upload_idrequired | string | An uploaded video (kind: "video"). An image upload returns 400. |
prompt | string | Optional guidance, max 1,000 characters. |
duration | number | Seconds, 0.3–12. |
name | string | Max 60 characters. |
max_cost_usd | number | Stop the job if its model spend passes this. |
Batches and suggestions#
Several motions at once#
POST/mascots/{id}/animations/batch takes up to 8 prompts (and an optional shared duration) and starts one job each. The whole batch is checked against your balance up front (20 credits per prompt), so it either starts completely or returns 402.
curl -X POST https://lumorig.com/api/v1/mascots/msc_…/animations/batch -H "Authorization: Bearer $LUMORIG_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "prompts": ["peeks out from the left", "typing furiously", "shrugs, then smiles"] }'
# 202 → { "object": "list", "data": [{ "id": "job_…", "type": "animate", … }, …] }Signature-motion ideas#
POST/mascots/{id}/suggestions proposes motions that fit this character and where it will appear. Pass an optional use_case (max 500 characters). Unlike other jobs, this request waits (up to about 90 seconds) and returns the finished job, whose result.suggestions is a list of { name, prompt, moment }. It costs 5 credits. Feed any prompt back into a motion from words.
curl -X POST https://lumorig.com/api/v1/mascots/msc_…/suggestions -H "Authorization: Bearer $LUMORIG_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "use_case": "onboarding guide in a budgeting app" }'Managing animations#
| Endpoint | Does |
|---|---|
GET/mascots/{id}/animations | List saved animations (without their clips). |
GET/mascots/{id}/animations/{animationId} | One animation, including its clip. |
DELETE/mascots/{id}/animations/{animationId} | Remove it from the mascot and future bundles. |
Anywhere the API asks for an animation you can pass an animation id, a saved name (the newest wins if two share a name), or a preset id. Names are what the player uses: <lumorig-player animation="bow">.
Hand-authored clips#
Send { "clip": { … }, "name": "…" } to save an AnimationClip you wrote or edited yourself. It is validated against the rig (known parts and states, rotation within joint limits, looping tracks that start and end on the same value) and saved with source: "custom", free and instantly. Problems come back as a 400 with a list in error.details.