Creatures
Create and manage animals and creatures over REST: generate main images, angle, pose and variation variants and motion clips, and give a creature a voice.
The Creatures API manages animals and other non-human beings with a locked look: a pet, a dragon, a mascot. A creature has an approved main image, a written description and variant images, and image and video nodes reuse it so the creature looks the same in every shot. Creatures work like objects, with three additions: a free-text species, named boards, and an optional voice.
The routes work on every edition and take a bearer token: a personal API token (ndr_…), an OAuth app token (ndr_app_…), or your session token on Community edition. See Authentication. The CLI has no creature commands; use REST, the SDK or MCP.
Endpoints
| Method | Path | What it does |
|---|---|---|
GET | /v1/creatures | List your creatures. |
GET | /v1/creatures/:id | Get one creature with its jobs in progress. |
POST | /v1/creatures | Create a creature, or update one when the body has an id. |
DELETE | /v1/creatures/:id | Archive a creature. It can be restored. |
DELETE | /v1/creatures/:id?permanent=true | Delete an archived creature and its files for good. |
POST | /v1/creatures/:id/restore | Restore an archived creature. |
POST | /v1/generate-creature | Generate 1 to 10 candidate main images. |
POST | /v1/generate-creature-asset | Generate one angle, pose, variation or custom variant. |
POST | /v1/generate-creature-motion | Animate the main image into a motion clip. |
POST | /v1/creatures/:id/approve-main-image | Approve a candidate as the main image and write the creature's description. |
POST | /v1/creatures/:id/llm-caption | Write the description again from the current main image. |
What a creature holds
| Field | What it holds |
|---|---|
id, name, description | The identifier, the display name and identity notes. |
species | Free text, for example dragon, wolf or tabby cat. It is the subject of the main-image prompt. |
category, style | Free-text category, and the visual style: realistic, anime, 3d-pixar or illustration. |
sourceImageUrl | The anchor main image, set when you approve a candidate. |
canonicalDescription | A visual description of about 80 to 120 words, written by Nodaro when the main image is approved. |
styleLock | Whether variants are generated from the main image. true by default. |
angles, poses, variations, motionClips | The asset buckets. Each entry is { name, url }; motionClips holds videos. |
boards | Up to 24 named boards: dense reference sheets, one per look or mood. |
voice | The creature's voice, or null. |
referencePhotos | Up to 20 mood-board photos, each { kind, url }. kind is front, side, detail, context, moodBoard or other. |
pendingJobs | On GET /v1/creatures/:id only: the variant jobs still running. |
List and read creatures
GET /v1/creatures returns your active creatures. It takes the same parameters as the object list: archived=true, projectId, q, and an optional limit (at most 500) with cursor. q keeps only the creatures whose name or description contains every word of it, ignoring case: the same search as characters. Without limit you get the full list; with it, one page and a nextCursor to pass back until it is null.
GET /v1/creatures/:id returns one creature. An archived creature returns 404 not_found. Each row, in the list and by id, also carries its Nodaro Keepers shelf state: catalog, catalogSort and catalogCategory. They are false, 0 and null on your own rows and on every copy.
curl "https://app.nodaro.ai/v1/creatures?limit=50" \
-H "Authorization: Bearer $NODARO_API_KEY"import { createClient, StaticTokenAuth } from '@nodaro/sdk'
const client = createClient({
baseUrl: 'https://app.nodaro.ai',
auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
})
const { creatures, nextCursor } = await client.creatures.list({ limit: 50 })
const { creatures: archived } = await client.creatures.listArchived()Create or update a creature
POST /v1/creatures creates a creature when the body has no id, and updates it when it has one. A create needs nodeId and name; use any label for nodeId, such as "scripted", when there is no canvas node. A create returns { id }, and an update returns { id, updatedAt }.
On an update, only the fields you send are written. The asset buckets are never written by an update, but boards is yours to set: send the whole list to replace it. Send expectedUpdatedAt to refuse the update with 409 concurrent_modification when someone changed the creature since you read it.
curl -X POST https://app.nodaro.ai/v1/creatures \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"nodeId": "scripted",
"name": "Ember",
"species": "red dragon",
"description": "Young dragon with copper scales and a chipped left horn",
"style": "realistic"
}'const { id } = await client.creatures.create({
nodeId: 'scripted',
name: 'Ember',
species: 'red dragon',
description: 'Young dragon with copper scales and a chipped left horn',
style: 'realistic',
})
await client.creatures.update(id, {
voice: { voiceId: 'Callum', voiceName: 'Callum', traits: 'gravelly, slow', voiceType: 'premade' },
})Prop
Type
Boards
A board is a dense reference sheet of the creature for one look or mood. Render one with the generate-image/creature-board preset of Generate Image, then save its URL in boards. See Presets and Reference boards.
Generate main images and variants
POST /v1/generate-creature starts one job per candidate and returns jobIds; a single-candidate request also returns jobId. With attachToCreatureId and a count of 1, the result becomes the main image when the job completes. With several candidates nothing is attached; approve the one you prefer. An optional subject of picker ids describes who the picture shows, as on POST /v1/generate-character.
POST /v1/generate-creature-asset generates one variant and returns { jobId }. assetType is angles, poses, variations or custom. Send attachToCreatureId, attachToColumn (angles, poses or variations) and attachName to append the result to a bucket; a custom variant must name its column.
curl -X POST https://app.nodaro.ai/v1/generate-creature \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Ember",
"species": "red dragon",
"count": 1,
"attachToCreatureId": "0b8d6f4a-2c1e-4b9d-8f3a-7e5c1d9b3f2a"
}'
curl -X POST https://app.nodaro.ai/v1/generate-creature-asset \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Ember",
"assetType": "poses",
"variant": "wings spread",
"attachToCreatureId": "0b8d6f4a-2c1e-4b9d-8f3a-7e5c1d9b3f2a",
"attachToColumn": "poses",
"attachName": "wings spread"
}'const { jobIds } = await client.creatures.generate({
name: 'Ember',
species: 'red dragon',
count: 4,
})
await client.creatures.generateAsset({
name: 'Ember',
assetType: 'poses',
variant: 'wings spread',
attachToCreatureId: id,
attachToColumn: 'poses',
attachName: 'wings spread',
})| Field | Route | What it does |
|---|---|---|
name | both | Required. The creature name. |
species, description, category, style | both | The creature's identity. |
count | main image | Candidates to generate, 1 to 10. The default is 1. |
assetType, variant | variant | Required. The kind of variant and its name. |
provider | both | The image model id. Omit it for the default model. |
sourceImageUrl | both | An image to start from or to vary. |
seedPromptHint | both | A prompt fragment to fold into the prompt, for example a choice from the Animal picker. |
Animate the main image
POST /v1/generate-creature-motion turns an image of the creature into a clip, such as an idle loop, a prowl or an attack, and returns { jobId }. sourceImageUrl is required. With attachToCreatureId and attachName, the clip is appended to motionClips.
curl -X POST https://app.nodaro.ai/v1/generate-creature-motion \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Ember",
"motionPrompt": "slow idle breathing, tail sways, smoke curls from the nostrils",
"sourceImageUrl": "https://cdn.nodaro.ai/creatures/ember-main.png",
"attachToCreatureId": "0b8d6f4a-2c1e-4b9d-8f3a-7e5c1d9b3f2a",
"attachName": "idle"
}'await client.creatures.generateMotion({
name: 'Ember',
motionPrompt: 'slow idle breathing, tail sways, smoke curls from the nostrils',
sourceImageUrl: ember.sourceImageUrl!,
provider: 'kling-turbo',
duration: 5,
attachToCreatureId: id,
attachName: 'idle',
})| Field | What it does |
|---|---|
name, motionPrompt, sourceImageUrl | Required. The creature name, the movement and the start frame. |
provider | kling-turbo (the default), kling, kling-3.0, hailuo-2.3, wan-i2v or seedance-2-mini. |
duration | The clip length in seconds. It must be a length the model offers; omit it for the model's default. |
aspectRatio | 1:1 (the default), 3:4, 16:9, 9:16 or 4:3. |
refineFromVideoUrl | An existing clip to refine with the new prompt instead of starting again from the image. |
attachToCreatureId, attachName | The creature and the clip's name in motionClips. |
| Model | Maker | Modes | Credits | Details |
|---|---|---|---|---|
| Kling 2.5 Turbo Pro | Kuaishou | Image to video, Text to video | from 121 | Faster Kling — good quality at lower cost. Supports end frame. |
| Kling 2.6 | Kuaishou | Image to video, Text to video | from 152 | Kling 2.6 I2V — strong motion realism. 5s/10s, optional native audio. |
| Kling 3.0 | Kuaishou | Image to video, Text to video | from 297 | Premium Kling 3.0 — variable 3-15s duration, native audio, 720P/1080P. |
| Hailuo 2.3 Standard | MiniMax | Image to video | from 83 | Cheaper Hailuo 2.3 tier — good baseline quality. |
| Wan 2.6 I2V | Alibaba | Image to video | from 193 | Wan 2.6 image-to-video — 5/10/15s at 720p/1080p. |
| Seedance 2 Mini | Bytedance | Image to video, Text to video | from 132 | Budget Seedance 2 tier — 480p/720p only, per-second pricing by resolution. |
seedance-2-mini is Seedance 2 Mini. Seedance 1.5 was retired in October 2026: a request that still names seedance runs, and is charged, as seedance-2-mini.
Approve a main image
POST /v1/creatures/:id/approve-main-image with { candidateJobId, expectedUpdatedAt? } sets a completed candidate as the main image and writes canonicalDescription in the same call. It returns { sourceImageUrl, canonicalDescription }. When writing the description fails, the main image is still set and the description is empty; the SDK returns null.
POST /v1/creatures/:id/llm-caption writes the description again and returns { canonicalDescription }. It answers 502 when the description cannot be written and 400 main_image_required when there is no main image yet. Both routes are safe to repeat. The approval itself is free. The description is a Claude Sonnet 4.6 vision call, charged for the tokens it used at that model's rate. See How LLM calls are charged. An approval is never refused for credits. With too little balance for the description, the image is still approved, the description is skipped, and llm-caption can write it later. A description the provider billed is charged even when it comes back empty. One the provider did not bill is refunded.
curl -X POST https://app.nodaro.ai/v1/creatures/0b8d6f4a-2c1e-4b9d-8f3a-7e5c1d9b3f2a/approve-main-image \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "candidateJobId": "1c9e7a5b-3d2f-4c8e-9a4b-8f6d2e1a4c3b" }'const ember = await client.creatures.get(id)
const approved = await client.creatures.approveMainImage(id, jobIds[0], ember.updatedAt)
if (approved.canonicalDescription === null) await client.creatures.recaption(id)approve-main-image and llm-caption both change the creature. With an OAuth app token, they need the assets:write scope. A token without it gets 403 insufficient_scope. Your session token and personal API tokens need no scope.
Archive, restore and delete a creature
| Action | curl | TypeScript SDK |
|---|---|---|
| Archive | DELETE /v1/creatures/:id | client.creatures.delete(id) |
| Restore | POST /v1/creatures/:id/restore | client.creatures.restore(id) |
| Delete for good | DELETE /v1/creatures/:id?permanent=true | client.creatures.permanentDelete(id) |
Archive returns { success: true, archived: true }, and repeating it changes nothing. Restore returns { id, name }, with a (restored) suffix when an active creature has the same name. Delete for good works only on an archived creature (400 not_archived otherwise) and removes the creature and every file it references that is yours. A file that another user's generation made, or that another user's upload folder or library holds, stays in storage and never counted against your storage quota.
Make a creature talk
No creature-specific route is needed. Render speech with the creature's voice, then lip-sync it onto the main image:
Render the speech
Run Text to Speech with the creature's voice.voiceId as voice, and its voice.ttsProvider and voice.voiceType when they are set.
Lip-sync the main image
Run Lip Sync with the creature's sourceImageUrl as imageUrl and the speech as audioUrl.
const speech = await client.nodes.runAndWait('text-to-speech', {
text: 'I knocked the vase off the shelf. I regret nothing.',
voice: ember.voice!.voiceId,
provider: ember.voice!.ttsProvider,
voiceType: ember.voice!.voiceType,
})
const clip = await client.nodes.runAndWait('lip-sync', {
imageUrl: ember.sourceImageUrl!,
audioUrl: speech.audioUrl,
provider: 'kling-avatar',
})See Run a single node for nodes.runAndWait and the raw POST /v1/<node-type> routes.
Put a creature in a shot
On Generate Image, pass the creature as a structured reference with source: "wired-creature". The creature is attached automatically, with wording that keeps its anatomy, markings and coloring. You can also name it in the prompt: @ember:1 places it where you type it. A role picks what to take from the image, for example @ember:1:markings. The roles are creature, anatomy, markings, pose, color and style.
{
"prompt": "a wide shot of @ember:1 landing on the castle wall",
"connectedReferences": [
{ "id": "cr-1", "defaultName": "Ember", "source": "wired-creature", "url": "https://cdn.nodaro.ai/creatures/ember-main.png" }
]
}In a workflow, wire the Animal/Creature Asset node into the image or video node instead.
Use it from MCP
| Tool | What it does |
|---|---|
list_creatures, get_creature | Find a creature and read its variant URLs and voice. |
generate_creature | Generate a main image (kind: "main") or a variant (kind: "asset"). |
approve_creature_main_image, recaption_creature | Approve a main image, or write its description again. |
generate_creature_motion | Animate the main image. |
See the MCP tools reference.
Credits
On Nodaro Cloud, a main-image request costs the image model's price times count, reserved before the first job starts. A variant costs the image model's price, and a motion clip the video model's image-to-video price. The approval is free. Its description, on approval and on each llm-caption call, is a Claude Sonnet 4.6 vision call, charged for the tokens it used. An approval is never refused for credits: with too little balance, the description is skipped.
Errors
| Status | Code | Meaning |
|---|---|---|
400 | validation_error | A field is missing or invalid, or duration is not a length the model offers. |
400 | not_archived | A permanent delete was sent for a creature that is not archived. |
400 | main_image_required | llm-caption was called before the creature has a main image. |
401 | unauthorized | The token is missing, invalid or revoked. |
402 | insufficient_credits | Nodaro Cloud only. The account cannot cover the reservation. |
404 | not_found | No active creature with that id belongs to you. |
409 | concurrent_modification | expectedUpdatedAt no longer matches. Read the creature again, merge and retry. |
502 | — | The canonical description could not be written. Try again. |
Frequently asked questions
What is the difference between a creature and an object?
A creature is an animal or a non-human being. It has a free-text species, a poses bucket instead of materials, named boards and an optional voice. Otherwise creatures work like objects, with the same routes for creating, generating, approving and archiving.
How do I make a creature talk?
Give the creature a voice, render speech with the Text to Speech node using that voice, then run Lip Sync with the creature's main image and the speech. No creature-specific route is needed.
Which models can animate a creature?
The same six video models as objects, by provider id kling-turbo (the default), kling, kling-3.0, hailuo-2.3, wan-i2v and seedance-2-mini. Each clip costs that model's image-to-video price.
Is there a CLI command for creatures?
No. Use the REST routes, the TypeScript SDK (client.creatures) or the creature tools over MCP.
Related
Animals and creatures
Animal/Creature Asset
Objects
Characters
Lip Sync
Last updated on
Locations
Script locations over REST: create and archive places, generate establishing shots, time-of-day, weather and angle variants, 360-degree views and motion clips.
Presets
Create, update, import, reorder and delete your node presets over REST, read the public built-in catalog with example pictures, and manage preset favorites.