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, and an optional limit (at most 500) with cursor. 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.
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.
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, minimax, hailuo-2.3, wan-i2v, seedance or bytedance-lite. |
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 110 | Faster Kling — good quality at lower cost. Supports end frame. |
| Kling 2.6 | Kuaishou | Image to video, Text to video | from 138 | Kling 2.6 I2V — strong motion realism. 5s/10s, optional native audio. |
| Kling 3.0 | Kuaishou | Image to video, Text to video | from 270 | Premium Kling 3.0 — variable 3-15s duration, native audio, 720P/1080P. |
| Hailuo 02 I2V Pro | MiniMax | Image to video, Text to video | 143 | Hailuo 02 Pro — strong photoreal motion, fixed 5-second clips. Supports end frame. |
| Hailuo 2.3 Standard | MiniMax | Image to video | from 75 | Cheaper Hailuo 2.3 tier — good baseline quality. |
| Wan 2.6 I2V | Alibaba | Image to video | from 175 | Wan 2.6 image-to-video — 5/10/15s at 720p/1080p. |
| Bytedance Lite I2V | Bytedance | Image to video, Text to video | 57 | Cheapest Bytedance video tier with end-frame support. |
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 free and safe to repeat.
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)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.
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. Approval and captioning are free.
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
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
Read your saved node presets, preset folders and the built-in preset catalog over REST, apply a preset to a node, and manage preset favorites.