NodaroAccount
Nodaro Docs
DocumentationNode ReferenceModelsTemplatesAI Agents (MCP)DevelopersSelf-hostingResearchCompare
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, 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',
})
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, hailuo-2.3, wan-i2v or seedance-2-mini.
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 121Faster Kling — good quality at lower cost. Supports end frame.
Kling 2.6KuaishouImage to video, Text to videofrom 152Kling 2.6 I2V — strong motion realism. 5s/10s, optional native audio.
Kling 3.0KuaishouImage to video, Text to videofrom 297Premium Kling 3.0 — variable 3-15s duration, native audio, 720P/1080P.
Hailuo 2.3 StandardMiniMaxImage to videofrom 83Cheaper Hailuo 2.3 tier — good baseline quality.
Wan 2.6 I2VAlibabaImage to videofrom 193Wan 2.6 image-to-video — 5/10/15s at 720p/1080p.
Seedance 2 MiniBytedanceImage to video, Text to videofrom 132Budget 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

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 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

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. 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

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

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.

Last updated on

On this page