# Pipelines

> Inicie um pipeline História → vídeo via REST, acompanhe e aprove as etapas, converse com o diretor e derive um pipeline concluído a partir de uma etapa.

Source: https://nodaro.ai/pt-BR/docs/developers/api/pipelines

A **API de pipelines** executa o **História → vídeo** (Story → Video) a partir do código. Você envia uma história de uma linha. O mecanismo de pipeline escreve um roteiro, cria o elenco, os adereços e os lugares e planeja as tomadas. Depois, ele renderiza quadros-chave, anima esses quadros com som e junta o filme. Cada etapa pode parar para a sua aprovação, ser executada sozinha ou abrir um chat com o diretor.

Os pipelines só rodam no Nodaro Cloud; as edições Community e Business retornam `403 edition_required`. Em uma instalação self-hosted, monte os mesmos passos como um workflow, com nós como [**Gerar roteiro** (Generate Script)](https://nodaro.ai/docs/nodes/video/generate-script), [**Gerar imagem** (Generate Image)](https://nodaro.ai/docs/nodes/image/generate-image) e [**Gerar vídeo** (Generate Video)](https://nodaro.ai/docs/nodes/video/generate-video). As rotas recebem um bearer token; os tokens de app OAuth precisam dos escopos listados abaixo. Veja [Autenticação](https://nodaro.ai/docs/developers/api/authentication).

## Endpoints
| Método | Caminho | Escopo | O que faz |
| --- | --- | --- | --- |
| `POST` | `/v1/pipelines` | `pipelines:execute` | Cria e inicia um pipeline. |
| `GET` | `/v1/pipelines` | `pipelines:read` | Lista os seus pipelines, do mais recente para o mais antigo. |
| `GET` | `/v1/pipelines/:id` | `pipelines:read` | Retorna o status, a etapa atual e os créditos. |
| `GET` | `/v1/pipelines/:id/events` | `pipelines:read` | Transmite os eventos do pipeline (server-sent events). |
| `GET` | `/v1/pipelines/:id/stages/:stage` | `pipelines:read` | Retorna o status, a saída e o feedback do revisor de uma etapa. |
| `GET` | `/v1/pipelines/:id/pending-approvals` | `pipelines:read` | Lista as etapas que aguardam aprovação. |
| `GET` | `/v1/pipelines/:id/timeline` | `pipelines:read` | Retorna o filme montado: cenas, durações e áudio. |
| `GET` | `/v1/pipelines/:id/entities?type=` | `pipelines:read` | Lista os personagens, os objetos, os locais ou as cenas do pipeline. |
| `POST` | `/v1/pipelines/:id/stages/:stage/approve` | `pipelines:approve` | Aprova uma etapa, opcionalmente com edições. |
| `POST` | `/v1/pipelines/:id/stages/:stage/reject` | `pipelines:approve` | Rejeita o roteiro com um feedback, para que ele seja escrito de novo. |
| `POST` | `/v1/pipelines/:id/sub-gates/:gate/approve` | `pipelines:approve` | Aprova um ponto de verificação dentro da etapa de animação. |
| `POST` | `/v1/pipelines/:id/sub-gates/:gate/reject` | `pipelines:approve` | Rejeita esse ponto de verificação e interrompe o pipeline. |
| `POST` | `/v1/pipelines/:id/entities/:sceneId/helpers/accept_match_cut_break` | `pipelines:approve` | Aceita uma quebra de match cut na etapa de imagens das cenas. |
| `POST` | `/v1/pipelines/:id/stages/:stage/chat` | `pipelines:approve` | Envia uma mensagem ao diretor (modo guiado). |
| `GET` | `/v1/pipelines/:id/stages/:stage/chat` | `pipelines:read` | Lê o chat de uma etapa. |
| `POST` | `/v1/pipelines/:id/stages/:stage/chat/turns/:turnId/apply` | `pipelines:approve` | Aplica uma alteração que o diretor propôs. |
| `POST` | `/v1/pipelines/:id/branch` | `pipelines:execute` | Executa de novo um pipeline concluído a partir de uma etapa, como um novo pipeline. |
| `POST` | `/v1/pipelines/:id/fork` | `pipelines:execute` | Interrompe o pipeline e mantém o canvas dele como nós comuns. |
| `POST` | `/v1/pipelines/:id/cancel` | `pipelines:execute` | Cancela um pipeline em execução e reembolsa os créditos não gastos. |

## Etapas e modos
Um pipeline passa por oito etapas, em ordem. `:stage` em um caminho é um destes nomes.

| Etapa | O que produz |
| --- | --- |
| `script` | O plano da história: o título, as cenas, o elenco, os locais e os adereços. |
| `characters` | Um personagem para cada papel do elenco. |
| `objects` | Os adereços de que a história precisa. |
| `locations` | Os lugares por onde a história passa. |
| `shot_list` | As tomadas de cada cena, com as escolhas de câmera e de continuidade. |
| `scene_images` | Um quadro-chave para cada tomada. |
| `animate_audio_edit` | As tomadas animadas, com diálogos, narração, música e a edição. |
| `post_merge` | O filme final, já juntado. |

O `mode` que você escolhe na criação decide quem faz o pipeline avançar:

| Modo | O que acontece |
| --- | --- |
| `manual` (padrão) | Toda etapa para em `awaiting_approval`. Você aprova, edita ou rejeita a etapa, e o pipeline segue. |
| `auto` | O mecanismo executa cada etapa sozinho. Revisores automáticos verificam o roteiro, a cobertura do elenco, dos locais e dos adereços, e os quadros-chave. Depois de 3 vereditos bloqueantes seguidos, o pipeline falha e os créditos não gastos são reembolsados. Ele só para em uma [quebra de match cut](#match-cut-breaks-in-the-scene-images-stage), até que todas as quebras sejam aceitas. Enquanto isso, o status continua `running`, e `pending-approvals` lista a etapa `scene_images`. |
| `guided` | Como `manual`, mais um chat com o diretor nas etapas `script` e `post_merge`. |

## Iniciar um pipeline
`POST /v1/pipelines` cria o pipeline, reserva os créditos dele e o inicia. A rota responde `201` com `{ id }`.

**curl**

```bash
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"
}'
```

**TypeScript SDK**

```ts

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',
})
```

```json
{ "id": "c2f8a4e6-1b9d-4f3a-8c7e-6d2b9f1a5e3c" }
```

<TypeTable
type={{
story_prompt: { type: 'string', description: 'Sobre o que é o filme, até 4.000 caracteres.', required: true },
format: { type: "'trailer' | 'short_film' | 'music_video' | 'reel' | 'commercial'", description: 'O tipo de filme. Ele define a duração permitida.', required: true },
target_duration_seconds: { type: 'integer', description: 'A duração do filme em segundos, dentro da faixa do formato. No máximo 600.', required: true },
root_node_id: { type: 'string', description: 'Um ID para a raiz do pipeline. Quando nenhum nó do canvas iniciou o pipeline, envie qualquer ID único, como um novo UUID.', required: true },
mode: { type: "'manual' | 'auto' | 'guided'", description: 'Quem faz o pipeline avançar.', default: 'manual' },
output_resolution: { type: "'480p' | '720p' | '1080p' | '4K'", description: 'A resolução do filme final.', default: '720p' },
language: { type: 'string', description: 'O idioma do roteiro e dos diálogos.', default: 'en' },
workflow_id: { type: 'string (uuid)', description: 'Um workflow seu no qual montar o filme.' },
config: { type: 'object', description: 'Flags opcionais: music_enabled, narration_enabled e lipsync_enabled, cada uma true por padrão.' },
max_cost_credits: { type: 'integer', description: 'Um teto para os créditos que o pipeline pode gastar.' },
}}
/>

Cada formato permite uma faixa de durações:

| Formato | Mínimo | Máximo |
| --- | --- | --- |
| `reel` | 7 s | 90 s |
| `commercial` | 10 s | 90 s |
| `trailer` | 30 s | 180 s |
| `short_film` | 12 s | 600 s |
| `music_video` | 30 s | 600 s |

## Acompanhar um pipeline
`GET /v1/pipelines/:id` retorna o estado do pipeline. Consulte a rota periodicamente, a cada poucos segundos, ou abra `GET /v1/pipelines/:id/events` para receber as mudanças como server-sent events.

```json
{
"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` é `queued`, `running`, `awaiting_approval`, `completed`, `failed`, `cancelled` ou `forked`. `failure_reason` explica um pipeline `failed`.

Quando o pipeline está `completed`, `GET /v1/pipelines/:id/timeline` retorna o filme como dados que você pode renderizar ou entregar a um editor de vídeo:

```json
{
"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"
}
```

Enquanto a etapa de animação está em execução, a linha do tempo também traz `animateProgress`: `{ totalShots, shotsDone, percent }`. Para finalizar a montagem em um editor externo, veja [Exportar uma linha do tempo](https://nodaro.ai/docs/developers/api/studio-productions#export-a-timeline-to-an-editing-app).

**curl**

```bash
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"
```

**TypeScript SDK**

```ts
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)
}
```

## Aprovar ou rejeitar uma etapa
Nos modos `manual` e `guided`, cada etapa para em `awaiting_approval`. `GET /v1/pipelines/:id/pending-approvals` lista as etapas que aguardam, cada uma como `{ stage_name, output }`. Leia uma etapa completa com `GET /v1/pipelines/:id/stages/:stage`, que retorna `{ status, output, critic_feedback }`.

- **Aprovar.** `POST /v1/pipelines/:id/stages/:stage/approve` retorna `{ ok: true }`, e o pipeline segue em frente. Para alterar antes a saída da etapa, envie `{ edits }`: um JSON Patch aplicado à saída antes da aprovação.
- **Rejeitar.** Só o roteiro pode ser rejeitado. `POST /v1/pipelines/:id/stages/script/reject` com `{ feedback }` retorna `{ ok: true }`, e o mecanismo escreve o roteiro de novo levando em conta a sua observação. Qualquer outra etapa retorna `400 stage_not_implemented`. Você pode rejeitar o roteiro no máximo duas vezes, e menos vezes se o revisor automático de roteiro já o tiver revisado.

**curl**

```bash
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." }'
```

**TypeScript SDK**

```ts
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.')
```

### Pontos de verificação dentro da etapa de animação
Nos modos `manual` e `guided`, a etapa `animate_audio_edit` pode parar em dois pontos de verificação próprios:

- **`dialogue_recheck`.** A etapa compara a duração real dos diálogos com o plano e ajusta o tempo das cenas. Quando uma cena não consegue ficar dentro de uma margem de 10% da duração-alvo, a etapa espera a sua aprovação.
- **`silent_cut_preview`.** A etapa monta uma prévia da montagem sem música e espera a sua aprovação antes de gerar a música e de pagar por ela.

`POST /v1/pipelines/:id/sub-gates/:gate/approve` retoma a etapa e retorna `{ ok: true, gate, resumed_at }`. `POST /v1/pipelines/:id/sub-gates/:gate/reject` faz a etapa e o pipeline falharem e reembolsa os créditos não gastos. No modo `auto`, a etapa continua sem parar.

### Quebras de match cut na etapa de imagens das cenas
A etapa `scene_images` pode parar em um terceiro ponto de verificação, `match_cut_break_pending`. Ela para ali em todos os modos, inclusive `auto`, quando um match cut planejado entre duas tomadas não se sustenta. A saída da etapa lista as tomadas em `match_cut_break_pending`.

As rotas de sub-gate acima não o liberam. Elas retornam `400 invalid_sub_gate`, e a mensagem de erro indica a rota que libera. Aceite cada quebra com `POST /v1/pipelines/:id/entities/:sceneId/helpers/accept_match_cut_break` e `{ "shotId": "<shot id>" }`. `sceneId` é o `id` da cena que contém a tomada, não o `metadata.scene_id` dela. `GET /v1/pipelines/:id/entities?type=scene` lista as cenas, cada uma com as tomadas dela em `metadata.scene_node_data.shots`.

Aceitar uma quebra exige o escopo `pipelines:approve` e não custa créditos. A chamada retorna `{ ok: true, pendingRemaining }`, o número de quebras que ainda aguardam. Quando a última é aceita, a etapa continua.

## Conversar com o diretor
No modo `guided`, uma etapa que aguarda aprovação tem um chat. Envie uma mensagem de até 8.000 caracteres com `POST /v1/pipelines/:id/stages/:stage/chat` e `{ message }`. O diretor responde em uma frase e pode propor uma alteração.

```json
{
"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` é `null`, um `edit_artifact` com um `json_patch`, ou um `suggest_branch` com `from_stage` e um `reason`.

| Etapa | O que o diretor pode propor | Turnos |
| --- | --- | --- |
| `script` | Uma edição do plano (um JSON Patch no título, nas cenas, no elenco, nos locais ou nos adereços), ou uma derivação quando a mudança é estrutural demais para um patch. | 20 por pipeline |
| `post_merge` | Um diagnóstico do filme final e uma derivação a partir de uma etapa anterior. O filme em si não pode receber patch. | 8 por pipeline |

Para aceitar uma proposta, chame `POST /v1/pipelines/:id/stages/:stage/chat/turns/:turnId/apply`. A rota retorna `{ applied: true, attemptId, newOutput }` e aprova a etapa. Quando a mudança quebra o plano, por exemplo ao remover um personagem que uma cena ainda usa, ela retorna `{ applied: false, error }`, e o diretor adiciona um turno que explica o que corrigir. Uma proposta inválida, ou uma etapa que não aguarda mais, retorna `409`. `GET /v1/pipelines/:id/stages/:stage/chat` retorna `{ turns }`, a conversa inteira.

Um pipeline guiado reserva créditos extras para o chat quando começa. Os créditos não usados são reembolsados.

**curl**

```bash
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." }'
```

**TypeScript SDK**

```ts
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)
}
```

## Derivar um pipeline concluído
`POST /v1/pipelines/:id/branch` executa de novo um pipeline `completed` a partir de uma etapa, como um novo pipeline. As etapas anteriores a `fromStage` são copiadas como aprovadas, `fromStage` começa a ser executada, e as etapas seguintes são criadas do zero. O pipeline original continua `completed`.

A derivação copia os personagens, os objetos e os locais para o novo pipeline, mas reutiliza os arquivos de imagem deles, então nenhum arquivo é duplicado. Ela começa com o chat vazio.

**curl**

```bash
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" }'
```

**TypeScript SDK**

```ts
const branch = await client.pipelines.branch(id, { fromStage: 'scene_images' })
console.log(branch.pipelineId, branch.clonedStages)
```

A rota responde `201` com `{ pipelineId, clonedStages, clonedEntities }`: o ID do novo pipeline, as etapas copiadas como aprovadas e o número de personagens, objetos e locais copiados. `fromStage` é qualquer nome de etapa da tabela acima.

## Cancelar ou ramificar um pipeline
- **Cancelar.** `POST /v1/pipelines/:id/cancel` interrompe um pipeline em execução, reembolsa os créditos reservados para o trabalho que não foi executado e retorna `{ ok: true }`. Um pipeline que já foi concluído, falhou ou foi cancelado retorna `409 already_terminal`, e nada muda.
- **Ramificar.** `POST /v1/pipelines/:id/fork` tira o canvas do controle do pipeline. Cada nó que ele criou vira um nó comum que você pode editar, os créditos não gastos são reembolsados, e o status passa a ser `forked`. Uma ramificação não pode ser desfeita. Para continuar com o mecanismo, inicie um novo pipeline.

## Créditos
O pipeline reserva o custo estimado quando começa; `upfront_credit_estimate` mostra esse valor. `spent_credits` e `reserved_credits` mostram em que ponto a execução está. Um pipeline cancelado ou que falhou reembolsa o que não gastou, e `max_cost_credits` limita o total. Veja [Créditos](https://nodaro.ai/docs/developers/api/credits).

## Usar pelo MCP e pelo SDK
O SDK expõe todas as rotas como `client.pipelines.*`. A CLI não tem comandos de pipeline. Os assistentes de IA usam estas ferramentas MCP:

| Ferramenta | Escopo | O que faz |
| --- | --- | --- |
| `start_pipeline` | `pipelines:execute` | Inicia um pipeline. O modo padrão da ferramenta é `auto`. |
| `get_pipeline_status` | `pipelines:read` | Lê o status, a etapa e os créditos. |
| `pipeline_pending_approvals` | `pipelines:read` | Lista as etapas que aguardam aprovação. |
| `chat_pipeline_stage`, `apply_chat_proposal` | `pipelines:approve` | Conversa com o diretor e aplica uma proposta. |
| `get_pipeline_stage_chat` | `pipelines:read` | Lê o chat de uma etapa. |
| `branch_pipeline` | `pipelines:execute` | Deriva um pipeline concluído. |

Veja [Film Director](https://nodaro.ai/docs/mcp/film-director) para o jeito guiado de dirigir um filme a partir de um assistente.

## Erros
| Status | Código | Significado |
| --- | --- | --- |
| `400` | `validation_error` | O corpo é inválido, por exemplo uma duração abaixo de 5 ou acima de 3.600 segundos, ou um `fromStage` de derivação que não é uma etapa. |
| `400` | `duration_out_of_bounds` | A duração está fora da faixa do formato, ou acima de 600 segundos. |
| `400` | `pipeline_not_completed` | Foi pedida uma derivação de um pipeline que não está `completed`. |
| `400` | `invalid_stage` | O envio ou a leitura de um chat indica uma etapa diferente de `script`, `shot_list` ou `post_merge`. |
| `400` | `invalid_stage_name` | Uma aprovação indica uma etapa que não é uma das oito. A leitura dessa etapa retorna `404` com este código. |
| `400` | `stage_not_implemented` | Uma chamada de rejeição indica uma etapa diferente de `script`. |
| `400` | `invalid_change_type_for_stage` | Foi proposto um patch para a etapa `post_merge`, que só aceita uma derivação. |
| `400` | `invalid_sub_gate` | O ponto de verificação não é um dos que as rotas de sub-gate resolvem. Para `match_cut_break_pending`, a mensagem indica a rota que aceita cada quebra. |
| `400` | `shot_not_found` | A tomada a aceitar não está nessa cena. |
| `400` | `not_a_match_cut` | A tomada a aceitar não tem um match cut planejado. |
| `400` | `invalid_entity_type` | A lista de entidades foi pedida com um `type` diferente de `character`, `object`, `location` ou `scene`. |
| `401` | `unauthorized` | O token está ausente, é inválido ou foi revogado. |
| `402` | `insufficient_credits` | A conta não tem créditos para cobrir a reserva. |
| `403` | `edition_required` | A instância não é o Nodaro Cloud. |
| `403` | `insufficient_scope` | Um token de app OAuth não tem o escopo da rota. |
| `404` | `not_found` | Nenhum pipeline com esse ID pertence a você. Em uma chamada de sub-gate, também significa que a etapa não está parada nesse ponto de verificação. |
| `404` | `pipeline_not_found` | Uma chamada de derivação indica um pipeline que não existe ou não é seu. |
| `404` | `stage_not_started` | A etapa que você leu ainda não começou. |
| `404` | `stage_not_found` | Uma chamada de sub-gate chegou a um pipeline cuja etapa de animação ainda não começou. Uma aprovação ou rejeição de etapa retorna `409` com este código. |
| `404` | `scene_not_found` | O pipeline não tem cena com esse ID. |
| `409` | `patch_invalid` | Uma proposta do chat não é uma alteração válida e não pôde ser aplicada. |
| `409` | `stage_not_awaiting` | A etapa não aguarda mais aprovação, quando você aplica uma proposta do chat ou aprova com edições. |
| `409` | `stage_already_advanced` | A etapa não aguarda mais aprovação, quando você aprova sem edições ou rejeita. |
| `409` | `stage_not_awaiting_approval` | Uma chamada de sub-gate chegou a uma etapa de animação que não aguarda aprovação. |
| `409` | `already_terminal` | Uma chamada de cancelamento chegou a um pipeline que já foi concluído, falhou ou foi cancelado. |
| `409` | `critic_retry_cap_reached` | O roteiro já foi revisado duas vezes, por você ou pelo revisor automático de roteiro. |
| `409` | `scene_not_planned` | A cena ainda não tem um plano de tomadas, então nenhuma quebra pode ser aceita nela. |
| `501` | `chat_not_wired_for_stage` | A etapa não tem um chat funcionando. Hoje, isso vale só para `shot_list`. |

## Frequently asked questions

### O que um pipeline História → vídeo produz?

Um filme pronto e o workflow por trás dele. O pipeline escreve um roteiro, cria os personagens, os objetos e os locais, planeja as tomadas, renderiza quadros-chave, anima esses quadros com som e junta o resultado. GET /v1/pipelines/:id/timeline retorna as cenas montadas.

### Um pipeline pode ser executado sem que ninguém o aprove?

Sim. Crie o pipeline com mode igual a auto, e o mecanismo executa cada etapa sozinho, com revisores automáticos verificando o resultado. Consulte periodicamente GET /v1/pipelines/:id até o status ser completed ou failed. A execução só para quando um match cut planejado entre duas tomadas se quebra. O status então continua running, e GET /v1/pipelines/:id/pending-approvals lista a etapa scene_images até todas as quebras serem aceitas.

### Como executar de novo um filme concluído a partir de uma etapa?

Chame POST /v1/pipelines/:id/branch com a etapa a partir da qual executar de novo. O Nodaro cria um novo pipeline que mantém as etapas anteriores e executa de novo essa etapa e as seguintes. O original não muda.

### O que acontece com os meus créditos quando cancelo um pipeline?

Os créditos reservados para o trabalho que ainda não foi executado são reembolsados. Os créditos do trabalho já realizado continuam cobrados. Um pipeline que já foi concluído, falhou ou foi cancelado não pode ser cancelado. A chamada retorna 409 already_terminal e não muda nada.

### Posso executar pipelines em uma instalação self-hosted?

Não. Os pipelines só rodam no Nodaro Cloud, e as edições self-hosted retornam 403 edition_required. Em uma instalação self-hosted, monte os mesmos passos como um workflow no canvas.
