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

Pipelines

Start a Story to Video pipeline over REST, follow its stages, approve or reject each gate, chat with the director, and branch a finished pipeline from a stage.

Available on Nodaro Cloud

The Pipelines API runs Story → Video from code. You send a one-line story. The pipeline engine writes a script, creates the cast, props and places, and plans the shots. It then renders keyframes, animates them with sound and merges the film. Each stage can stop for your approval, run by itself, or open a chat with the director.

Pipelines run on Nodaro Cloud only; Community and Business editions answer 403 edition_required. On a self-hosted install, build the same steps as a workflow with nodes such as Generate Script, Generate Image and Generate Video. The routes take a bearer token; OAuth app tokens need the scopes listed below. See Authentication.

Endpoints

MethodPathScopeWhat it does
POST/v1/pipelinespipelines:executeCreate and start a pipeline.
GET/v1/pipelinespipelines:readList your pipelines, most recent first.
GET/v1/pipelines/:idpipelines:readGet the status, the current stage and the credits.
GET/v1/pipelines/:id/eventspipelines:readStream the pipeline's events (server-sent events).
GET/v1/pipelines/:id/stages/:stagepipelines:readGet one stage's status, output and reviewer feedback.
GET/v1/pipelines/:id/pending-approvalspipelines:readList the stages waiting for approval.
GET/v1/pipelines/:id/timelinepipelines:readGet the assembled film: scenes, durations and audio.
POST/v1/pipelines/:id/stages/:stage/approvepipelines:approveApprove a stage, optionally with edits.
POST/v1/pipelines/:id/stages/:stage/rejectpipelines:approveReject a stage with feedback, so it runs again.
POST/v1/pipelines/:id/sub-gates/:gate/approvepipelines:approveApprove a checkpoint inside the animate stage.
POST/v1/pipelines/:id/sub-gates/:gate/rejectpipelines:approveReject that checkpoint and stop the pipeline.
POST/v1/pipelines/:id/stages/:stage/chatpipelines:approveSend a message to the director (guided mode).
GET/v1/pipelines/:id/stages/:stage/chatpipelines:readRead the chat of a stage.
POST/v1/pipelines/:id/stages/:stage/chat/turns/:turnId/applypipelines:approveApply a change the director proposed.
POST/v1/pipelines/:id/branchpipelines:executeRe-run a completed pipeline from a stage, as a new pipeline.
POST/v1/pipelines/:id/forkpipelines:executeStop the pipeline and keep its canvas as ordinary nodes.
POST/v1/pipelines/:id/cancelpipelines:executeCancel a running pipeline and refund unspent credits.

Stages and modes

A pipeline walks eight stages in order. :stage in a path is one of these names.

StageWhat it produces
scriptThe story plan: the title, the scenes, the cast, the locations and the props.
charactersA character for each role in the cast.
objectsThe props the story needs.
locationsThe places the story visits.
shot_listThe shots of each scene, with their camera and continuity choices.
scene_imagesA keyframe for each shot.
animate_audio_editThe animated shots, with dialogue, narration, music and the edit.
post_mergeThe final merged film.

The mode you choose at creation decides who moves the pipeline forward:

ModeWhat happens
manual (default)Every stage stops at awaiting_approval. You approve it, edit it or reject it, and the pipeline goes on.
autoThe engine runs every stage by itself. Automatic reviewers check the script, the coverage of the cast, locations and props, and the keyframes. After 3 blocking verdicts in a row, the pipeline fails and the unspent credits are refunded.
guidedLike manual, plus a chat with the director on the script and post_merge stages.

Start a pipeline

POST /v1/pipelines creates the pipeline, reserves its credits and starts it. It answers 201 with { id }.

curl -X POST https://app.nodaro.ai/v1/pipelines \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "root_node_id": "0e6b2f7c-4a1d-4c8e-9b3f-5d7a2c1e8f4b",
    "story_prompt": "A lighthouse keeper must restart the light before the storm hits.",
    "format": "short_film",
    "target_duration_seconds": 60,
    "mode": "auto",
    "output_resolution": "720p"
  }'
import { createClient, StaticTokenAuth } from '@nodaro/sdk'

const client = createClient({
  baseUrl: 'https://app.nodaro.ai',
  auth: new StaticTokenAuth(process.env.NODARO_API_KEY!),
})

const { id } = await client.pipelines.create({
  root_node_id: crypto.randomUUID(),
  story_prompt: 'A lighthouse keeper must restart the light before the storm hits.',
  format: 'short_film',
  target_duration_seconds: 60,
  mode: 'auto',
})
{ "id": "c2f8a4e6-1b9d-4f3a-8c7e-6d2b9f1a5e3c" }

Prop

Type

Each format allows a range of lengths:

FormatShortestLongest
reel7 s90 s
commercial10 s90 s
trailer30 s180 s
short_film12 s600 s
music_video30 s600 s

Follow a pipeline

GET /v1/pipelines/:id returns the pipeline's state. Poll it every few seconds, or open GET /v1/pipelines/:id/events to receive the changes as server-sent events.

{
  "id": "c2f8a4e6-1b9d-4f3a-8c7e-6d2b9f1a5e3c",
  "status": "running",
  "current_stage": "scene_images",
  "mode": "auto",
  "spent_credits": 412,
  "reserved_credits": 1180,
  "upfront_credit_estimate": 1650,
  "failure_reason": null,
  "current_progress_message": "Rendering keyframe 5 of 9",
  "branched_from_pipeline_id": null,
  "branched_from_stage": null
}

status is queued, running, awaiting_approval, completed, failed, cancelled or forked. failure_reason explains a failed pipeline.

When the pipeline is completed, GET /v1/pipelines/:id/timeline returns the film as data you can render or hand to an editor:

{
  "fps": 24,
  "width": 1280,
  "height": 720,
  "scenes": [
    { "compositeUrl": "https://cdn.nodaro.ai/pipelines/scene-1.mp4", "durationSeconds": 8.5 },
    { "compositeUrl": "https://cdn.nodaro.ai/pipelines/scene-2.mp4", "durationSeconds": 11 }
  ],
  "musicUrl": "https://cdn.nodaro.ai/pipelines/score.mp3",
  "narrationUrl": "https://cdn.nodaro.ai/pipelines/narration.mp3"
}

While the animate stage runs, the timeline also carries animateProgress: { totalShots, shotsDone, percent }. To finish the cut in an external editor, see Export a timeline.

curl https://app.nodaro.ai/v1/pipelines/c2f8a4e6-1b9d-4f3a-8c7e-6d2b9f1a5e3c \
  -H "Authorization: Bearer $NODARO_API_KEY"

curl https://app.nodaro.ai/v1/pipelines/c2f8a4e6-1b9d-4f3a-8c7e-6d2b9f1a5e3c/timeline \
  -H "Authorization: Bearer $NODARO_API_KEY"
const pipeline = await client.pipelines.get(id)
console.log(pipeline.status, pipeline.current_stage)

const timeline = await client.pipelines.getTimeline(id)
for (const scene of timeline.scenes) {
  console.log(scene.compositeUrl, scene.durationSeconds)
}

Approve or reject a stage

In manual and guided mode, each stage stops at awaiting_approval. GET /v1/pipelines/:id/pending-approvals lists the stages waiting, each { stage_name, output }. Read a stage in full with GET /v1/pipelines/:id/stages/:stage, which returns { status, output, critic_feedback }.

  • Approve. POST /v1/pipelines/:id/stages/:stage/approve returns { ok: true } and the pipeline moves on. To change the stage's output first, send { edits }: a JSON Patch applied to the output before approval.
  • Reject. POST /v1/pipelines/:id/stages/:stage/reject with { feedback } returns { ok: true }. The engine runs the stage again and takes your note into account.
curl -X POST https://app.nodaro.ai/v1/pipelines/c2f8a4e6-1b9d-4f3a-8c7e-6d2b9f1a5e3c/stages/script/approve \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "edits": [{ "op": "replace", "path": "/title", "value": "The Last Light" }] }'

curl -X POST https://app.nodaro.ai/v1/pipelines/c2f8a4e6-1b9d-4f3a-8c7e-6d2b9f1a5e3c/stages/script/reject \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "feedback": "Make the story darker and more suspenseful." }'
const approvals = await client.pipelines.pendingApprovals(id)
const { output } = await client.pipelines.getStage(id, 'script')

await client.pipelines.approveStage(id, 'script', [
  { op: 'replace', path: '/title', value: 'The Last Light' },
])
// or
await client.pipelines.rejectStage(id, 'script', 'Make the story darker and more suspenseful.')

Checkpoints inside the animate stage

In manual and guided mode, the animate_audio_edit stage can stop at two checkpoints of its own:

  • dialogue_recheck. The stage compares the real length of the dialogue with the plan and adjusts the scene timing. When a scene cannot stay within 10% of its target, the stage waits for your approval.
  • silent_cut_preview. The stage assembles a preview of the cut without music and waits for your approval before it generates, and pays for, the music.

POST /v1/pipelines/:id/sub-gates/:gate/approve resumes the stage and returns { ok: true, gate, resumed_at }. POST /v1/pipelines/:id/sub-gates/:gate/reject fails the stage and the pipeline, and refunds the unspent credits. In auto mode, the stage continues without stopping.

Chat with the director

In guided mode, a stage waiting for approval has a chat. Send a message of up to 8,000 characters with POST /v1/pipelines/:id/stages/:stage/chat and { message }. The director answers in one sentence and may propose a change.

{
  "turnId": "f1a3c5e7-9b2d-4f6a-8c1e-3d5b7f9a2c4e",
  "role": "assistant",
  "content": "I moved the keeper's reason for staying into scene 2 and tightened the ending.",
  "proposed_change": {
    "change_type": "edit_artifact",
    "json_patch": [{ "op": "replace", "path": "/scenes/1/description", "value": "The keeper finds his late wife's log and decides to stay." }]
  }
}

proposed_change is null, an edit_artifact with a json_patch, or a suggest_branch with from_stage and a reason.

StageWhat the director can proposeTurns
scriptAn edit to the plan (a JSON Patch on the title, the scenes, the cast, the locations or the props), or a branch when the change is too structural to patch.20 per pipeline
post_mergeA diagnosis of the final film and a branch from an earlier stage. The film itself cannot be patched.8 per pipeline

To accept a proposal, call POST /v1/pipelines/:id/stages/:stage/chat/turns/:turnId/apply. It returns { applied: true, attemptId, newOutput } and approves the stage. When the change breaks the plan, for example by removing a character a scene still uses, it returns { applied: false, error } and the director adds a turn that explains what to fix. A proposal that is not valid, or a stage that is no longer waiting, answers 409. GET /v1/pipelines/:id/stages/:stage/chat returns { turns }, the whole conversation.

A guided pipeline reserves 40 extra credits for the chat when it starts. Unused credits are refunded.

curl -X POST https://app.nodaro.ai/v1/pipelines/c2f8a4e6-1b9d-4f3a-8c7e-6d2b9f1a5e3c/stages/script/chat \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "message": "Make the keeper'\''s motivation clearer in scene 2." }'
const reply = await client.pipelines.chatStage(id, 'script', "Make the keeper's motivation clearer in scene 2.")
if (reply.proposed_change) {
  const result = await client.pipelines.applyChatProposal(id, 'script', reply.turnId)
  if (!result.applied) console.log(result.error.code)
}

Branch a finished pipeline

POST /v1/pipelines/:id/branch re-runs a completed pipeline from one stage, as a new pipeline. The stages before fromStage are copied as approved, fromStage starts running, and the stages after it are created fresh. The original pipeline stays completed.

The branch copies the characters, objects and locations into the new pipeline but reuses their image files, so no file is duplicated. It starts with an empty chat.

curl -X POST https://app.nodaro.ai/v1/pipelines/c2f8a4e6-1b9d-4f3a-8c7e-6d2b9f1a5e3c/branch \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "fromStage": "scene_images" }'
const branch = await client.pipelines.branch(id, { fromStage: 'scene_images' })
console.log(branch.pipelineId, branch.clonedStages)

The route answers 201 with { pipelineId, clonedStages, clonedEntities }: the new pipeline's id, the stages copied as approved, and the number of characters, objects and locations copied. fromStage is any stage name from the table above.

Cancel or fork a pipeline

  • Cancel. POST /v1/pipelines/:id/cancel stops a running pipeline, refunds the credits reserved for work that has not run, and returns { ok: true }. Cancelling a finished pipeline changes nothing.
  • Fork. POST /v1/pipelines/:id/fork takes the canvas away from the pipeline. Every node it created becomes an ordinary node you can edit, the unspent credits are refunded, and the status becomes forked. A fork cannot be undone. To continue with the engine, start a new pipeline.

Credits

The pipeline reserves its estimated cost when it starts; upfront_credit_estimate shows it. spent_credits and reserved_credits show where the run stands. A cancelled or failed pipeline refunds what it did not spend, and max_cost_credits caps the total. See Credits.

Use it from MCP and the SDK

The SDK wraps every route as client.pipelines.*. The CLI has no pipeline commands. AI assistants use these MCP tools:

ToolScopeWhat it does
start_pipelinepipelines:executeStart a pipeline. Its default mode is auto.
get_pipeline_statuspipelines:readRead the status, the stage and the credits.
pipeline_pending_approvalspipelines:readList the stages waiting for approval.
chat_pipeline_stage, apply_chat_proposalpipelines:approveChat with the director and apply a proposal.
get_pipeline_stage_chatpipelines:readRead a stage's chat.
branch_pipelinepipelines:executeBranch a completed pipeline.

See Film Director for the guided way to direct a film from an assistant.

Errors

StatusCodeMeaning
400validation_errorThe body is invalid, for example a length outside the format's range.
400pipeline_not_completedA branch was requested from a pipeline that is not completed.
400invalid_stageThe stage name is not one of the eight stages.
400invalid_change_type_for_stageA patch was proposed for the post_merge stage, which accepts only a branch.
401unauthorizedThe token is missing, invalid or revoked.
402insufficient_creditsThe account cannot cover the reservation.
403edition_requiredThe instance is not Nodaro Cloud.
403insufficient_scopeAn OAuth app token lacks the route's scope.
404pipeline_not_foundNo pipeline with that id belongs to you.
409patch_invalidA chat proposal is not a valid change and could not be applied.
409stage_not_awaitingThe stage is no longer waiting for approval.
501chat_not_wired_for_stageThe stage has no chat.

Frequently asked questions

Last updated on

On this page