Nodaro Docs
DocumentationNode ReferenceModelsAI Agents (MCP)DevelopersSelf-hostingResearch
REST API

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

MethodPathWhat it does
GET/v1/creaturesList your creatures.
GET/v1/creatures/:idGet one creature with its jobs in progress.
POST/v1/creaturesCreate a creature, or update one when the body has an id.
DELETE/v1/creatures/:idArchive a creature. It can be restored.
DELETE/v1/creatures/:id?permanent=trueDelete an archived creature and its files for good.
POST/v1/creatures/:id/restoreRestore an archived creature.
POST/v1/generate-creatureGenerate 1 to 10 candidate main images.
POST/v1/generate-creature-assetGenerate one angle, pose, variation or custom variant.
POST/v1/generate-creature-motionAnimate the main image into a motion clip.
POST/v1/creatures/:id/approve-main-imageApprove a candidate as the main image and write the creature's description.
POST/v1/creatures/:id/llm-captionWrite the description again from the current main image.

What a creature holds

FieldWhat it holds
id, name, descriptionThe identifier, the display name and identity notes.
speciesFree text, for example dragon, wolf or tabby cat. It is the subject of the main-image prompt.
category, styleFree-text category, and the visual style: realistic, anime, 3d-pixar or illustration.
sourceImageUrlThe anchor main image, set when you approve a candidate.
canonicalDescriptionA visual description of about 80 to 120 words, written by Nodaro when the main image is approved.
styleLockWhether variants are generated from the main image. true by default.
angles, poses, variations, motionClipsThe asset buckets. Each entry is { name, url }; motionClips holds videos.
boardsUp to 24 named boards: dense reference sheets, one per look or mood.
voiceThe creature's voice, or null.
referencePhotosUp to 20 mood-board photos, each { kind, url }. kind is front, side, detail, context, moodBoard or other.
pendingJobsOn 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',
})
FieldRouteWhat it does
namebothRequired. The creature name.
species, description, category, stylebothThe creature's identity.
countmain imageCandidates to generate, 1 to 10. The default is 1.
assetType, variantvariantRequired. The kind of variant and its name.
providerbothThe image model id. Omit it for the default model.
sourceImageUrlbothAn image to start from or to vary.
seedPromptHintbothA 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',
})
FieldWhat it does
name, motionPrompt, sourceImageUrlRequired. The creature name, the movement and the start frame.
providerkling-turbo (the default), kling, kling-3.0, minimax, hailuo-2.3, wan-i2v, seedance or bytedance-lite.
durationThe clip length in seconds. It must be a length the model offers; omit it for the model's default.
aspectRatio1:1 (the default), 3:4, 16:9, 9:16 or 4:3.
refineFromVideoUrlAn existing clip to refine with the new prompt instead of starting again from the image.
attachToCreatureId, attachNameThe creature and the clip's name in motionClips.
ModelMakerModesCreditsDetails
Kling 2.5 Turbo ProKuaishouImage to video, Text to videofrom 110Faster Kling — good quality at lower cost. Supports end frame.
Kling 2.6KuaishouImage to video, Text to videofrom 138Kling 2.6 I2V — strong motion realism. 5s/10s, optional native audio.
Kling 3.0KuaishouImage to video, Text to videofrom 270Premium Kling 3.0 — variable 3-15s duration, native audio, 720P/1080P.
Hailuo 02 I2V ProMiniMaxImage to video, Text to video143Hailuo 02 Pro — strong photoreal motion, fixed 5-second clips. Supports end frame.
Hailuo 2.3 StandardMiniMaxImage to videofrom 75Cheaper Hailuo 2.3 tier — good baseline quality.
Wan 2.6 I2VAlibabaImage to videofrom 175Wan 2.6 image-to-video — 5/10/15s at 720p/1080p.
Bytedance Lite I2VBytedanceImage to video, Text to video57Cheapest 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

ActioncurlTypeScript SDK
ArchiveDELETE /v1/creatures/:idclient.creatures.delete(id)
RestorePOST /v1/creatures/:id/restoreclient.creatures.restore(id)
Delete for goodDELETE /v1/creatures/:id?permanent=trueclient.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

ToolWhat it does
list_creatures, get_creatureFind a creature and read its variant URLs and voice.
generate_creatureGenerate a main image (kind: "main") or a variant (kind: "asset").
approve_creature_main_image, recaption_creatureApprove a main image, or write its description again.
generate_creature_motionAnimate 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

StatusCodeMeaning
400validation_errorA field is missing or invalid, or duration is not a length the model offers.
400not_archivedA permanent delete was sent for a creature that is not archived.
400main_image_requiredllm-caption was called before the creature has a main image.
401unauthorizedThe token is missing, invalid or revoked.
402insufficient_creditsNodaro Cloud only. The account cannot cover the reservation.
404not_foundNo active creature with that id belongs to you.
409concurrent_modificationexpectedUpdatedAt no longer matches. Read the creature again, merge and retry.
502—The canonical description could not be written. Try again.

Frequently asked questions

Last updated on

On this page