# Nós

> Execute qualquer nó do Nodaro com POST /v1/<node-type>, descubra nós, modelos e valores de seletores e guie o prompt com referências e IDs de direção.

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

Uma **execução de nó** chama um nó do Nodaro diretamente, sem montar um workflow: você envia `POST /v1/<node-type>` com as configurações do nó no corpo. Todos os nós de geração seguem esse formato, de `generate-image` e `generate-video` a `text-to-speech`, e os endpoints de descoberta informam quais nós, modelos e configurações existem. A maioria das execuções de nó é assíncrona: a resposta é um `jobId`, que você consulta periodicamente até o resultado ficar pronto.

## Executar um nó
A rota de um tipo de nó é `POST /v1/` seguido do tipo, e o corpo são as configurações do nó em JSON:

```http
POST /v1/generate-image
Authorization: Bearer ndr_…
Content-Type: application/json

{ "prompt": "a lighthouse in a storm, oil painting", "provider": "nano-banana-pro", "aspectRatio": "3:4" }
```

O que volta depende do nó:

- Os **nós de geração** respondem `200` com `{ "jobId": "…" }`. O processamento roda em um worker, e você consulta o job periodicamente com `GET /v1/jobs/:id/status` até o status ser `completed`. Veja [Jobs](https://nodaro.ai/docs/developers/api/jobs).
- As **gerações de imagem e de vídeo** podem incluir `adjustments`, uma lista das configurações que o servidor corrigiu para o modelo escolhido. `generate-video` também pode incluir `warnings`. Veja [Correções de parâmetros](https://nodaro.ai/docs/developers/api/workflows#parameter-corrections).
- Os **nós inline**, como `combine-text`, retornam o resultado completo na hora, sem `jobId`.
- Os **extratores**, como `web-scrape`, também respondem na hora. A resposta traz um `jobId`, para o seu histórico, e também os próprios dados.

Uma geração reserva créditos quando começa. Quando a conta não tem créditos para cobrir a execução, a chamada retorna `402 insufficient_credits`.

### Nós com um caminho mais longo
A maioria dos nós de texto que chamam um modelo de linguagem segue a mesma regra `POST /v1/<node-type>`: `generate-script`, `image-critic`, `qa-check` e `describe-to-picker`. Alguns outros são registrados em um caminho mais longo:

| Tipo de nó | Rota |
| --- | --- |
| `llm-chat` | `POST /v1/llm-chat/generate` |
| `after-effects` | `POST /v1/after-effects/generate` |
| `motion-graphics` | `POST /v1/motion-graphics/generate` |
| `lottie-overlay` | `POST /v1/lottie-overlay/generate` |
| `3d-title` | `POST /v1/3d-title/generate` |
| `image-to-text` | `POST /v1/image-to-text/describe` |
| `video-composer` | `POST /v1/scene-graph/generate` |

O `client.nodes.run(type, params)` do SDK envia para `/v1/<type>`, então chame esses nós com `client.request('POST', '/v1/llm-chat/generate', { body })`.

As rotas de modelo de linguagem aceitam dois campos opcionais:

- **`reasoningEffort`**: `none`, `low`, `medium`, `high`, `xhigh` ou `max`, conforme o modelo. Omita o campo, ou escolha um nível que o modelo não aceita, para usar o padrão do próprio modelo. `xhigh` e `max` cobram um nível de crédito acima, com teto no nível premium.
- **`advancedMode: true`**: só para modelos Gemini. A requisição roda na API do próprio fabricante do modelo, a única via em que `temperature`, `maxTokens` e toda a faixa de raciocínio têm efeito. Ela cobra um nível de crédito acima, independentemente de `reasoningEffort`. O nível de crédito tem teto no premium. Um modelo sem essa via retorna `400 advanced_mode_unsupported`.

O `describe-to-picker` responde com o resultado na mesma requisição, e também pode transmitir a resposta como eventos enviados pelo servidor (server-sent events). Veja [**Descrever para seletor** (Describe to Picker)](https://nodaro.ai/docs/nodes/image/describe-to-picker#api). Os campos de `generate-script` estão em [**Gerar roteiro** (Generate Script)](https://nodaro.ai/docs/nodes/video/generate-script#api).

## Exemplo: gerar uma imagem
### Iniciar o job
**curl**

```bash
curl -s -X POST https://app.nodaro.ai/v1/generate-image \
  -H "Authorization: Bearer $NODARO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
"prompt": "a knight on a hill at dawn, cinematic",
"provider": "nano-banana-pro",
"aspectRatio": "16:9",
"resolution": "2K"
}'
```

```json
{ "jobId": "0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10" }
```

**TypeScript SDK**

```ts

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

const result = await client.nodes.run('generate-image', {
prompt: 'a knight on a hill at dawn, cinematic',
provider: 'nano-banana-pro',
aspectRatio: '16:9',
resolution: '2K',
})
```

**CLI**

```bash
nodaro nodes run generate-image \
  --param prompt="a knight on a hill at dawn, cinematic" \
  --param provider=nano-banana-pro \
  --param aspectRatio=16:9 \
  --param resolution=2K
```

### Consultar o job periodicamente
Peça o status do job a cada 2 a 5 segundos, até ele ser `completed` ou `failed`:

```bash
curl -s https://app.nodaro.ai/v1/jobs/0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10/status \
  -H "Authorization: Bearer $NODARO_API_KEY" | jq -r .data.status
```

### Ler o resultado
Um job de imagem concluído traz a URL da imagem em `output_data.imageUrl`. Um job de vídeo usa `videoUrl`, um de áudio usa `audioUrl`, e muitos jobs também trazem um `thumbnailUrl`.

```json
{
"data": {
"id": "0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10",
"status": "completed",
"progress": 100,
"output_data": { "imageUrl": "https://…/0f1a9c2e.png" },
"error_message": null
}
}
```

O SDK e a CLI podem fazer a consulta periódica por você:

**TypeScript SDK**

```ts
const output = await client.nodes.runAndWait('generate-image', {
prompt: 'a knight on a hill at dawn, cinematic',
provider: 'nano-banana-pro',
})
console.log(output.imageUrl)

// Several candidates at once, in input order:
const results = await client.nodes.runMany('generate-image', [
{ prompt: 'a knight on a hill, sunrise' },
{ prompt: 'a knight on a hill, golden hour' },
{ prompt: 'a knight on a hill, blue hour' },
])
for (const { jobId, output } of results) console.log(jobId, output.imageUrl)
```

**CLI**

```bash
nodaro nodes run generate-image \
  --param prompt="a knight on a hill at dawn, cinematic" \
  --param provider=nano-banana-pro \
  --watch --json | jq -r '.output_data.imageUrl'
```

Por padrão, `runAndWait` faz a consulta periódica a cada 2.000 ms, por até 15 minutos. Mude isso com `pollMs` e `maxMs`, interrompa com um `AbortSignal` em `signal` e acompanhe o progresso com `onProgress`. O método lança erros tipados que você pode capturar com `instanceof`:

| Erro | Quando |
| --- | --- |
| `InsufficientCreditsError`, `StorageExceededError`, `JobBlockedError` | A execução foi recusada antes de qualquer job começar. |
| `JobFailedError` | O job terminou como `failed` ou `cancelled`. O erro traz o `jobId` e a mensagem de erro. |
| `JobTimeoutError` | O tempo de `maxMs` passou. O job não é cancelado e geralmente ainda termina: busque-o depois com `client.jobs.get(jobId)`. |
| `JobAbortedError` | O seu `signal` foi disparado. |
| `JobHeldError` | O job entrou em `pending_review` em uma implantação que revisa os resultados. O job não é cancelado: verifique de novo mais tarde. |

Na CLI, passe corpos complexos, como arrays ou objetos aninhados, em um arquivo com `--params-file body.json`. Os valores das flags substituem os do arquivo para a mesma chave, e `true`, `false`, `null` e números são convertidos a partir do texto.

### Configurações do nó Gerar imagem
Os campos abaixo são as configurações mais usadas de `POST /v1/generate-image`. Leia [**Gerar imagem** (Generate Image)](https://nodaro.ai/docs/nodes/image/generate-image) para saber o que cada configuração faz no editor, e os [modelos de imagem](https://nodaro.ai/docs/models/image) para saber o que cada modelo aceita.

<TypeTable
type={{
prompt: { type: 'string', description: 'A descrição da imagem. Até 30.000 caracteres na requisição; cada modelo tem um limite próprio, menor, e um prompt mais longo é encurtado para caber.', required: true },
provider: { type: 'string', description: 'O ID do modelo, como nano-banana-pro ou gpt-image-2-5-flare. GET /v1/nodes/generate-image lista todos os IDs em providers.' },
aspectRatio: { type: 'string', description: 'auto, 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 5:4, 4:5, 21:9 e outros. Um valor que o modelo não aceita é corrigido.' },
resolution: { type: 'string', description: '1K, 2K ou 4K, ou 0.5 MP, 1 MP, 2 MP ou 4 MP, nos modelos compatíveis. Afeta o preço em créditos.' },
quality: { type: 'string', description: 'basic, medium ou high, nos modelos que têm níveis de qualidade. Afeta o preço em créditos.' },
negativePrompt: { type: 'string', description: 'O que evitar, até 5.000 caracteres.' },
seed: { type: 'integer', description: 'Um número fixo que torna uma execução repetível, nos modelos compatíveis.' },
referenceImageUrls: { type: 'string[]', description: 'Até 14 URLs de imagens de referência, com a mais importante primeiro.' },
connectedReferences: { type: 'object[]', description: 'Referências rotuladas que a rota monta no prompt. Veja Usar referências.' },
describedReferences: { type: 'object[]', description: 'Até 10 assuntos com nome para os quais você não tem imagem.' },
referenceOrder: { type: 'string[]', description: 'IDs de referências, na ordem em que as referências devem ser numeradas.' },
referenceLock: { type: 'string', description: 'standard ou multi-person. Adiciona uma redação testada de fidelidade às referências.' },
direction: { type: 'object', description: 'IDs de seletores para a câmera, a luz e o visual. Veja Direção cinematográfica por ID.' },
subject: { type: 'object', description: 'IDs de seletores para a pessoa, para o figurino e a beleza e para os objetos de cena da tomada.' },
baseImageUrl: { type: 'string', description: 'Uma imagem para editar, em vez de gerar do zero.' },
maskUrl: { type: 'string', description: 'Com baseImageUrl: uma máscara em que branco significa alterar e preto significa manter. Só a área branca é gerada de novo.' },
strength: { type: 'number', description: 'De 0 a 1. O quanto um refinamento pode se afastar de baseImageUrl, nos modelos compatíveis.' },
guidanceScale: { type: 'number', description: 'De 0 a 20, nos modelos compatíveis.' },
}}
/>

Uma edição com máscara ou um refinamento custa o mesmo que uma nova geração nesse modelo.

## Gerar um vídeo
Duas rotas geram vídeo. `POST /v1/generate-video` anima a partir de imagens: um quadro inicial em `imageUrl`, um quadro final opcional em `endFrameUrl`, ou só referências, nos modelos que as aceitam. `POST /v1/text-to-video` gera um clipe só a partir de um prompt, então o `prompt` dela é obrigatório. Quando você omite `provider`, a rota usa o modelo de vídeo padrão da plataforma.

```json
{
"imageUrl": "https://…/frame.png",
"provider": "seedance-2",
"prompt": "she turns toward the window",
"duration": 8,
"resolution": "720p",
"direction": { "cameraMotion": "dolly-in", "timeOfDay": "dawn" }
}
```

A resposta é `{ "jobId": "…" }`, com `warnings` e `adjustments` quando se aplicam. O job concluído traz `output_data.videoUrl`.

<TypeTable
type={{
imageUrl: { type: 'string', description: 'O quadro inicial. Obrigatório, a menos que o modelo consiga funcionar só com referências.' },
endFrameUrl: { type: 'string', description: 'O quadro final, nos modelos compatíveis com vídeo de primeiro e último quadro.' },
prompt: { type: 'string', description: 'O que acontece no clipe, até 30.000 caracteres na requisição. Os modelos de vídeo têm limites próprios bem menores.' },
provider: { type: 'string', description: 'O ID do modelo, como seedance-2, kling-3.0 ou veo3.1.' },
duration: { type: 'number', description: 'Segundos, de 1 a 60, conforme o modelo. -1 significa Automática (Auto) na família Seedance 2.' },
resolution: { type: 'string', description: 'Por exemplo, 480p, 720p, 1080p ou 4k. Um valor não aceito é ajustado para a faixa mais próxima que o modelo tem.' },
aspectRatio: { type: 'string', description: '16:9, 9:16, 1:1, 4:3, 3:4, 4:5, 5:4, 21:9, 9:21 ou adaptive, conforme o modelo.' },
generateAudio: { type: 'boolean', description: 'Pede som junto com o vídeo, nos modelos que conseguem gerá-lo.' },
negativePrompt: { type: 'string', description: 'O que evitar.' },
referenceImageUrls: { type: 'string[]', description: 'Imagens de referência, até 30 na requisição. Cada modelo aceita um máximo próprio.' },
referenceVideoUrls: { type: 'string[]', description: 'Clipes de referência, até 10, nos modelos que os aceitam.' },
referenceAudioUrls: { type: 'string[]', description: 'Áudio de referência, até 10, nos modelos que o aceitam.' },
connectedReferences: { type: 'object[]', description: 'Referências de imagem rotuladas. Veja Usar referências.' },
direction: { type: 'object', description: 'IDs de seletores para o movimento de câmera, o enquadramento, a luz e o visual.' },
subject: { type: 'object', description: 'IDs de seletores para a pessoa, para o figurino e a beleza e para os objetos de cena.' },
seed: { type: 'integer', description: 'Um número fixo para execuções repetíveis, nos modelos compatíveis.' },
}}
/>

Algumas regras mudam conforme o modelo:

- **Na família Seedance 2, `resolution` e `aspectRatio` são repassados como estão**: um valor que o modelo não aceita é ignorado, nunca recusado. O [Seedance 2](https://nodaro.ai/docs/models/video/seedance-2) é o único com `4k` e `adaptive`. O [Seedance 2.5](https://nodaro.ai/docs/models/video/seedance-2-5) gera até 30 segundos em uma chamada e, com um quadro inicial, sempre renderiza na proporção desse quadro.
- **O [MiniMax Hailuo 3](https://nodaro.ai/docs/models/video/minimax-h3) aceita `resolution` `2K` ou `768P`**, escritos exatamente assim. `GET /v1/nodes/:type` lista o valor a enviar em `providerResolutionWire`.
- **Quadros e referências podem ser combinados** no Seedance 2 e no MiniMax Hailuo 3. Quando você envia uma referência junto com um quadro inicial ou final, os quadros viram referências numeradas no prompt, em vez de pontos fixos de início e fim. O [Wan 3.0](https://nodaro.ai/docs/models/video/wan-3-0) não consegue enviar os dois, então o quadro é adicionado ao final da lista de referências, e a chamada continua sendo bem-sucedida.
- **Vídeos de referência custam mais.** Nos modelos que os cobram, um clipe de referência é precificado pela própria duração mais a duração da saída, então um clipe de origem mais longo reserva mais créditos. O Wan 3.0 cobra apenas os segundos de saída.

Para as durações, as resoluções e os preços em créditos de cada modelo, leia os [modelos de vídeo](https://nodaro.ai/docs/models/video) ou chame `GET /v1/models`.

## Usar referências
Referências são imagens (e, no vídeo, também clipes e áudio) que o modelo deve seguir. Você pode enviá-las como uma lista simples de URLs ou como **referências estruturadas**, que a rota numera, rotula e escreve no prompt por você, exatamente como o editor faz com os nós conectados.

### Referências estruturadas
`POST /v1/generate-image`, `POST /v1/generate-video`, `POST /v1/text-to-video` e `POST /v1/extend-video` aceitam `connectedReferences`. Cada entrada descreve uma imagem:

<TypeTable
type={{
id: { type: 'string', description: 'Um ID estável seu para a referência. Ele pode conter : e /, então uma URL de imagem é um ID válido.', required: true },
defaultName: { type: 'string', description: 'O nome da referência, por exemplo Maya ou Old Town.', required: true },
source: { type: 'string', description: 'manual, wired-image, wired-character, wired-face, wired-object, wired-creature ou wired-location.', required: true },
url: { type: 'string', description: 'A URL da imagem. Um endereço privado ou um esquema diferente de http ou https é recusado.', required: true },
description: { type: 'string', description: 'O rótulo que o prompt usa para esta referência.' },
defaultRole: { type: 'string', description: 'O que aproveitar da imagem quando uma menção não indica um papel, por exemplo background.' },
identityLock: { type: '{ enabled: boolean; text?: string }', description: 'Desativado por padrão. Quando ativado, adiciona uma linha curta que fixa exatamente a identidade desta referência. text substitui a redação padrão, e, nele, {ref} representa o vínculo com a referência.' },
descriptionOverride: { type: 'string', description: 'Até 2.000 caracteres. O que esta referência é, só nesta execução. Tem prioridade sobre a descrição salva do personagem ou do objeto.' },
}}
/>

Nas rotas de vídeo, a rota transforma essas entradas em referências numeradas:

- **Toda referência que você não menciona é anexada.** A URL dela entra na lista de referências, sem duplicatas, e ganha uma linha como `@image_1 (reference): <label>`. Já uma entrada `wired-character` passa a fazer parte de uma instrução “Use these characters:”.
- **A lista é cortada no limite do modelo** antes da numeração, então um número de referência no prompt nunca aponta para uma imagem que não foi enviada.
- **`{image:N:label}` no prompt** vira “the label from `@image_N`”, numerado em relação às referências anexadas.
- **`{ref:<id>}` e `{ref:<id>:label}`** apontam para uma referência pelo `id` que você deu a ela. A plataforma substitui o token pelo número da referência depois de numerar a lista, então você nunca calcula um número. A lista é numerada nesta ordem: primeiro a lista simples `referenceImageUrls`, depois os personagens que você não mencionou, depois as outras entradas, na sua ordem. Um token cuja referência não foi anexada, por ter passado do limite ou porque o modelo não aceita referências, vira o rótulo dele; sem rótulo, vira `defaultName`; sem nenhum dos dois, não vira nada. Ele nunca chega ao modelo como texto bruto.
- **`referenceOrder`**, uma lista de IDs de referências, reordena as referências e as renumera de acordo. `POST /v1/generate-image` também aceita esse campo.

`connectedReferences` só envia imagens. `referenceVideoUrls` e `referenceAudioUrls` continuam sendo listas simples. Se você omitir `connectedReferences`, as rotas se comportam como antes: o seu `prompt` e as suas `referenceImageUrls` são enviados como estão.

### Quais modelos de vídeo aceitam imagens de referência
| Família de modelos | Imagens de referência |
| --- | --- |
| Família [Seedance 2](https://nodaro.ai/docs/models/video/seedance-2) | Até 9 |
| [HappyHorse Ref2V](https://nodaro.ai/docs/models/video/happyhorse-1-1-ref2v) | Até 9 |
| [Gemini Omni](https://nodaro.ai/docs/models/video/gemini-omni), [Kling 3 Omni](https://nodaro.ai/docs/models/video/kling-3-omni), [Grok Imagine image-to-video](https://nodaro.ai/docs/models/video/grok-imagine-i2v) | Até 7 |
| [VEO 3.1 Fast](https://nodaro.ai/docs/models/video/veo-3-1-fast) e [VEO 3.1 Lite](https://nodaro.ai/docs/models/video/veo-3-1-lite) | Até 3 |

Em qualquer outro modelo, os tokens `{image:N}` são reduzidos aos rótulos e nada é anexado. O [VEO 3.1 Quality](https://nodaro.ai/docs/models/video/veo-3-1-quality) não está na lista: as referências enviadas com `veo3` são ignoradas, e a execução usa os quadros dela.

### Vídeo só com referências
Em `POST /v1/generate-video`, o quadro inicial é opcional quando o modelo aceita pelo menos um dos tipos de referência que você envia, por exemplo o Kling 3 Omni só com `referenceImageUrls`. Uma execução só com referências no VEO 3.1 Fast ou no Lite muda sozinha para o modo de referência, então você não precisa de `generationType`. Um tipo de referência que o modelo não consegue usar não conta: referências só de áudio em um modelo que aceita apenas imagens são recusadas com `400`.

Um **quadro final sozinho** é aceito nos modelos que o incorporam às referências: a família Seedance 2, o MiniMax Hailuo 3 e o Wan 3.0. Envie `endFrameUrl` uma vez, sem repetir a imagem em `referenceImageUrls`. O pacote `@nodaro/shared` exporta `videoProviderFoldsLoneEndFrame(provider)` para que a sua interface use a mesma regra.

Quando uma requisição não tem quadro inicial, nem modo de referência, nem uma referência que o modelo possa usar, a resposta depende do modelo:

- Um modelo que não consegue gerar vídeo só a partir de texto, como o Kling 3 Omni, o HappyHorse Ref2V ou o Hailuo 2.3, retorna `400 image_required`. A mensagem diz se referências funcionariam no lugar. `GET /v1/models` é a fonte oficial sobre quais são esses modelos.
- Todos os outros modelos retornam `400 validation_error` e indicam `POST /v1/text-to-video` para um clipe só com prompt.

### Estender vídeo
`POST /v1/extend-video` só aceita `connectedReferences` e `referenceImageUrls` com `provider: "seedance-2-extend"`. Qualquer outro modelo de extensão os recusa com `400`. O limite é de 8 imagens suas, porque o último quadro do clipe de origem ocupa uma vaga de referência, depois das suas, e assim os seus números nunca mudam. Os últimos 2 segundos da origem vão como `@video_1` e já estão incluídos no preço da extensão: imagens de referência não adicionam créditos. O nó **Estender vídeo** (Extend Video) não tem `direction` nem `subject`, porque o prompt dele continua um clipe que já tem um visual.

### Referências descritas
`describedReferences` nomeia um assunto que você consegue descrever, mas do qual ainda não tem imagem, como um papel em um roteiro. `POST /v1/generate-image`, `/v1/generate-video`, `/v1/text-to-video` e `/v1/extend-video` aceitam até 10 entradas `{ name, description }`. O nome pode ter até 80 caracteres, e a descrição, até 2.000.

- **Nada é anexado.** Uma referência descrita não ocupa vaga de referência, e a numeração das suas outras referências não muda.
- **Ela chega ao modelo como uma linha de texto:** `<Name> — <description>.` Mantenha o nome no seu prompt, por exemplo `Natalie walks down the pier.`, e a linha diz ao modelo quem é Natalie. Não escreva uma menção com `@` para ela: essa sintaxe aponta para uma imagem anexada.
- **Ela funciona sozinha.** Envie `describedReferences` sem nenhuma `connectedReferences`, o caso comum de uma história escrita antes de existir qualquer personagem.
- **Entradas sem nome ou sem descrição são descartadas**, e um nome repetido é escrito uma vez só. Todos os modelos de extensão aceitam referências descritas, porque elas não trazem URL.

### Descrições e legendas por uso
- **`descriptionOverride`**, em uma entrada de `connectedReferences`, diz o que a referência é só nesta execução, em até 2.000 caracteres. Ele preenche a descrição que a instrução da referência já tem, com prioridade sobre a descrição salva. Quando a instrução não tem descrição, ele adiciona uma linha, então o modelo recebe a informação uma vez, nunca duas.
- **`referenceVideoCaptions` e `referenceAudioCaptions`**, em `POST /v1/generate-video` e `/v1/text-to-video`, descrevem os clipes e os áudios de referência, com até 500 caracteres cada. Eles são alinhados por índice: `referenceVideoCaptions[0]` descreve `referenceVideoUrls[0]`. Cada um vira uma linha como `@video_1: <caption>.`, e uma entrada vazia pula um clipe sem quebrar o alinhamento.

### Mencionar uma referência no prompt
Em `POST /v1/generate-image`, você pode colocar uma referência dentro da sua frase, em vez de deixá-la na lista do final. Escreva `@<name-slug>:<index>`, ou `@<name-slug>:<index>:<role>` para dizer o que aproveitar dela:

```json
{
"prompt": "a wide shot of @nessie:1 rising beside @dock:2:material",
"connectedReferences": [
{ "id": "cr-1", "defaultName": "Nessie", "source": "wired-creature", "url": "https://…/nessie.png" },
{ "id": "ob-1", "defaultName": "Dock", "source": "wired-object", "url": "https://…/dock.png" }
]
}
```

O modelo recebe “a wide shot of the creature from reference image A rising beside the material from reference image B”.

- **O slug vem de `defaultName`**, em minúsculas, com cada sequência de outros caracteres trocada por um único `-`: `Old Town` vira `old-town`. Não existe campo de slug para definir.
- **O índice só associa a menção a uma referência.** Ele nunca é escrito no prompt, e a própria plataforma numera as referências.
- Os **papéis** para imagens (`manual` e `wired-image`) são `object`, `person`, `face`, `clothes`, `background`, `style`, `pose` e `texture`. Criaturas aceitam `creature`, `anatomy`, `markings`, `pose`, `color` e `style`. Objetos aceitam `object`, `shape`, `material`, `color`, `texture` e `style`. Qualquer outra palavra única passa como foi escrita. Sem papel, vale o `defaultRole` da entrada.
- **`~lock` e `~nolock`** depois de uma menção, como em `@town:1:background~lock`, ativam ou desativam o bloqueio de identidade dessa referência para a menção.
- **Os nomes são resolvidos nesta ordem:** personagem, local, imagem, criatura, objeto. Um nome compartilhado por um personagem e uma imagem indica o personagem, e, dentro de um mesmo tipo, vale a primeira correspondência.
- **Um nome cujo slug começa com um dígito não pode ser mencionado**, por exemplo `3D Render`, que vira `3d-render`. Renomeie a referência para mencioná-la. Uma menção a uma referência que passou do limite do modelo fica como texto literal.
- **Mencionar uma referência a move** da lista do final para o lugar onde você a digitou, e as letras das referências seguintes mudam para acompanhar a frase. Para uma criatura ou um objeto, a menção também substitui a linha que, sem ela, seria adicionada no final.

### Bloqueio de referência
`referenceLock`, em `POST /v1/generate-image`, adiciona antes da cena a redação testada do Nodaro para fidelidade às referências:

| Valor | Adiciona | Use para |
| --- | --- | --- |
| `standard` | Instruções para usar só o que as referências mostram, manter a semelhança e compor as referências juntas | Composições a partir de várias referências |
| `multi-person` | O mesmo, mais regras para nunca alterar nem misturar rostos | Dois ou mais rostos em uma tomada |

Você envia o ID, não o texto: a redação pertence à plataforma e melhora sem que você atualize o cliente. Se você omitir o campo, nenhum bloqueio é adicionado.

## Direção cinematográfica por ID
`direction` descreve a câmera, a luz e o visual com **IDs de seletores**, em vez de texto corrido. `POST /v1/generate-image`, `POST /v1/generate-video` e `POST /v1/text-to-video` aceitam o campo. O Nodaro escreve no prompt a própria redação testada para cada ID, então uma requisição salva incorpora as melhorias de redação com o tempo, em vez de congelar o texto que o seu cliente escreveu.

```json
{
"prompt": "a knight on a hill",
"provider": "nano-banana-pro",
"direction": {
"shotSize": "wide-shot",
"lens": "wide-24mm",
"lightingStyle": "rembrandt",
"style": "anime",
"mood": ["happy", "joyful"]
}
}
```

### Chaves e a origem dos IDs
Cada chave é um campo de um seletor de Controles criativos. Obtenha os IDs válidos em `GET /v1/picker-catalogs/<picker>`, os mesmos catálogos que os seletores do editor usam.

| Chaves | Seletor | Imagem | Vídeo |
| --- | --- | --- | --- |
| `shotSize`, `angle`, `coverage`, `composition`, `vantage` | [**Enquadramento** (Framing)](https://nodaro.ai/docs/nodes/creative-controls/framing) | Sim | Sim |
| `pose` | [**Pose**](https://nodaro.ai/docs/nodes/creative-controls/pose) | Sim | Sim |
| `compositionEffect` | [**Efeitos de composição** (Composition Effects)](https://nodaro.ai/docs/nodes/creative-controls/composition-effects) | Sim | Sim |
| `cameraFormat` | [**Câmera / película** (Camera / Film Stock)](https://nodaro.ai/docs/nodes/creative-controls/camera-format) | Sim | Sim |
| `lens` | [**Lente** (Lens)](https://nodaro.ai/docs/nodes/creative-controls/lens) | Sim | Sim |
| `aperture`, `shutterSpeed`, `isoValue` | [**Configurações de exposição** (Exposure Settings)](https://nodaro.ai/docs/nodes/creative-controls/exposure-settings) | Sim | Não |
| `timeOfDay`, `lightingStyle`, `lightingDirection`, `lightingRatio`, `colorTemperature` | [**Iluminação** (Lighting)](https://nodaro.ai/docs/nodes/creative-controls/lighting) | Sim | Sim |
| `colorLook` | [**Cor / look** (Color / Look)](https://nodaro.ai/docs/nodes/creative-controls/color-look) | Sim | Sim |
| `atmosphere` | [**Atmosfera** (Atmosphere)](https://nodaro.ai/docs/nodes/creative-controls/atmosphere) | Sim | Sim |
| `postProcess` | [**Efeitos de pós-produção** (Post-Process Effects)](https://nodaro.ai/docs/nodes/creative-controls/post-process-effects) | Sim | Não |
| `style` | [**Estilo** (Style)](https://nodaro.ai/docs/nodes/creative-controls/style) | Sim | Sim |
| `mood` | [**Clima** (Mood)](https://nodaro.ai/docs/nodes/creative-controls/mood) | Sim | Sim |
| `aesthetic` | [**Estética** (Aesthetic)](https://nodaro.ai/docs/nodes/creative-controls/aesthetic) | Sim | Sim |
| `photoGenre` | [**Gênero fotográfico** (Photo Genre)](https://nodaro.ai/docs/nodes/creative-controls/photo-genre) | Sim | Não |
| `photographer` | [**Fotógrafo** (Photographer)](https://nodaro.ai/docs/nodes/creative-controls/photographer) | Sim | Não |
| `renderQuality` | [**Qualidade de renderização** (Render Quality)](https://nodaro.ai/docs/nodes/creative-controls/render-quality) | Sim | Não |
| `setting` | [**Cenário** (Setting)](https://nodaro.ai/docs/nodes/creative-controls/setting) | Sim | Sim |
| `era` | [**Época** (Era)](https://nodaro.ai/docs/nodes/creative-controls/era) | Sim | Sim |
| `backdrop` | [**Fundo de estúdio** (Backdrop)](https://nodaro.ai/docs/nodes/creative-controls/backdrop) | Sim | Sim |
| `cameraMotion` | [**Movimento de câmera** (Camera Motion)](https://nodaro.ai/docs/nodes/creative-controls/camera-motion) | Não | Sim |
| `actionFx` | [**Efeitos de ação** (Action FX)](https://nodaro.ai/docs/nodes/creative-controls/action-fx) | Não | Sim |
| `temporalSpeed`, `temporalFreeze`, `temporalDirection`, `temporalShutter` | [**Efeitos temporais** (Temporal)](https://nodaro.ai/docs/nodes/creative-controls/temporal) | Não | Sim |
| `transition` | [**Transição** (Transition)](https://nodaro.ai/docs/nodes/creative-controls/transition) | Não | Sim |
| `loopSubject` | [**Tema de loop** (Loop Subject)](https://nodaro.ai/docs/nodes/creative-controls/loop-subject) | Não | Sim |

Uma chave que não se aplica a uma rota é aceita e simplesmente não adiciona nada, então um mesmo mapa de IDs de visual pode ser enviado sem alterações às rotas de imagem e de vídeo.

### Valores e limites
- **Um ID ou uma lista.** As chaves de escolha múltipla (`mood`, `aesthetic`, `photographer`, `atmosphere`, `postProcess`, `composition` e `lightingStyle`) aceitam IDs até o limite de cada uma, e os IDs excedentes são descartados. Uma chave de escolha única que recebe uma lista usa a primeira entrada.
- **Dois limites resultam em recusa** com `400 validation_error`: mais de 8 entradas em uma chave e um ID com mais de 100 caracteres.
- **Ausente não é vazio.** Uma chave ausente significa nenhuma indicação, nunca um valor padrão. Uma string vazia ou uma lista vazia não adiciona nada.
- **Chaves e IDs desconhecidos são ignorados**, não recusados. Um cliente mais novo em um servidor mais antigo recebe menos indicações em vez de um erro, então atualize o servidor antes de um cliente que envia chaves novas.
- **Pacotes de catálogo personalizados.** Em uma implantação que registra os próprios pacotes de catálogo, `GET /v1/picker-catalogs` lista os IDs que um pacote adiciona. Esses IDs são aceitos, mas não adicionam redação a `direction`.

### Onde entram as palavras
As cláusulas são adicionadas depois do seu prompt, em uma seção `[style]`. A linha de filme traz `cameraFormat`, `colorLook`, `style` e `era`, e a linha de cena traz as outras chaves de visual:

```text
a knight on a hill

[style]:
<film line>
<scene line>
```

- A ordem dentro de uma linha é a ordem fixa da plataforma, não a ordem das suas chaves. Uma cláusula repetida por duas chaves é escrita uma vez só.
- Uma linha vazia é omitida. Quando nenhuma chave adiciona nada, não há seção nenhuma, e o seu `prompt` chega ao modelo sem alterações.
- Nas rotas de vídeo, as chaves de movimento funcionam de outro jeito. Elas adicionam um termo profissional curto, como `cross-dissolve`, e ficam no corpo do prompt, depois do seu texto, porque o movimento faz parte da tomada. `cameraMotion` vem primeiro. Só as chaves de visual vão para a seção `[style]`.

### Quando o prompt é longo demais
Cada modelo aceita um prompt até um comprimento próprio. Um `direction` completo pode, sozinho, passar de um limite pequeno, por exemplo 3.000 caracteres no Seedream, do lado da imagem, e 1.000 caracteres no Kling, do lado do vídeo. Quando isso acontece, o Nodaro remove as cláusulas de direção uma de cada vez, a partir do final da sua ordem fixa, até o prompt caber. Nada mais é removido antes delas:

- As cláusulas de assunto só são removidas depois de todas as cláusulas de direção.
- O seu texto, as suas referências e as frases que as vinculam, as menções com `@` e as linhas `Style:` e `Avoid:` sempre ficam.
- Só quando o prompt ainda não cabe, sem nenhuma indicação restante, o final do texto é cortado, com `...` no fim.

Nas rotas de vídeo, o orçamento de caracteres também conta as instruções de referência que a rota adiciona, que nunca são removidas. Em um modelo sem configuração de prompt negativo, o seu `negativePrompt` é adicionado como uma linha `Avoid:` cujo espaço é reservado primeiro, então um prompt negativo longo custa cláusulas de indicação, não texto. O texto opcional de `injectCharacterContext` é adicionado depois dessa etapa e não entra no orçamento.

O job registra o que aconteceu. `input_data.prompt` é o que o modelo recebeu, `input_data.userPrompt` é o texto que você enviou (uma string vazia quando você enviou só `direction`), e `input_data.direction` são os seus IDs, como foram enviados.

### Direção salva em um nó
Um nó [Gerar imagem](https://nodaro.ai/docs/nodes/image/generate-image) em um workflow salvo pode guardar o mesmo objeto `direction` nos dados dele, escrito pela API, pelo MCP ou por um app que cria workflows. O editor respeita esse objeto em toda execução e na prévia do prompt final. Os IDs salvos se somam a qualquer seletor de Enquadramento, Iluminação ou Estilo conectado: a indicação do seletor conectado vem primeiro, depois os IDs salvos. As predefinições e as exportações de workflow mantêm os IDs junto com o resto do nó.

## Descrever o assunto por ID
`subject` é a mesma ideia para **quem está na tomada**: a pessoa, como ela está produzida e os objetos de cena no quadro. `POST /v1/generate-image`, `POST /v1/generate-video` e `POST /v1/text-to-video` aceitam o campo:

```json
{
"prompt": "on the seawall at dusk",
"provider": "nano-banana-pro",
"subject": {
"type": "woman",
"age": "age-30s",
"ethnicity": "east-asian",
"hairBase": "base-short-straight",
"makeup": "makeup-smoky",
"outerwear": "outerwear-trench",
"heldProp": "smartphone"
}
}
```

- **As chaves são os campos dos seletores [Pessoa (Person)](https://nodaro.ai/docs/nodes/creative-controls/person) e [Figurino e beleza (Styling)](https://nodaro.ai/docs/nodes/creative-controls/styling)**, como `type`, `age`, `ethnicity`, `faceShape`, `hairColor`, `skinTone`, `makeup`, `outfit`, `outerwear` e `footwear`. Há também três chaves de objetos: `heldProp` ([**Objeto na mão** (Held Prop)](https://nodaro.ai/docs/nodes/creative-controls/held-prop)), `material` ([**Material**](https://nodaro.ai/docs/nodes/creative-controls/material)) e `animal` ([**Animal**](https://nodaro.ai/docs/nodes/creative-controls/animal)). `GET /v1/picker-catalogs/person` e `/styling` listam todos os campos e IDs.
- **`subject` e `direction` nunca se sobrepõem.** As chaves de um e de outro são separadas, então uma escolha nunca adiciona duas cláusulas. `pose` pertence a `direction`.
- **`customAge` é o único número.** Envie `"age": "age-custom"` com `"customAge": 34` para uma idade exata em anos. O valor é arredondado e mantido entre 0 e 120.
- **As listas têm limites por chave.** `jewelry`, `wardrobeState` e `distinctiveFeature` aceitam 3 IDs. `ethnicity`, `regionalAesthetic`, `hairColor`, `eyeColor`, `lipState`, `eyeState`, `skinTexture`, `hairState`, `heldProp` e `material` aceitam 2. Todas as outras chaves, inclusive `animal`, aceitam 1. Os IDs excedentes são descartados.
- **Limites que resultam em recusa:** mais de 8 entradas em uma chave, um ID com mais de 100 caracteres, mais de 128 chaves ou uma chave com mais de 64 caracteres retornam `400 validation_error`.
- **Chaves desconhecidas são removidas, e IDs desconhecidos são ignorados.** `input_data.subject`, no job, registra exatamente os IDs que foram usados.
- **As cláusulas de assunto fazem parte do seu texto**, antes das cláusulas de direção e nunca na seção `[style]`. Pessoa vira uma cláusula, Figurino e beleza vira outra, e escolhas sobrepostas são escritas uma vez só. Na rota de imagem, cada escolha adiciona a cláusula completa; as rotas de vídeo adicionam o termo curto, porque o quadro inicial já mostra quem é o assunto.

## Descobrir nós
`GET /v1/nodes` lista todos os tipos de nó que o servidor conhece, e `GET /v1/nodes/:type` retorna um deles. Os dois são públicos, não precisam de token e ficam em cache por 5 minutos. Um tipo desconhecido retorna `404 not_found`.

**curl**

```bash
curl -s https://app.nodaro.ai/v1/nodes/generate-image | jq .data
```

**TypeScript SDK**

```ts
const { data: nodes } = await client.nodes.list()
const imageNodes = nodes.filter((n) => n.category === 'ai-image')

const { data: generateImage } = await client.nodes.get('generate-image')
console.log(generateImage.providers)
```

**CLI**

```bash
nodaro nodes list --category ai-image
nodaro nodes get generate-image
```

```json
{
"data": {
"type": "generate-image",
"label": "Generate Image",
"category": "ai-image",
"description": "Generate an image from a text prompt using an AI provider.",
"outputType": "image",
"creditCost": "3-682",
"providers": ["nano-banana-pro", "gpt-image-2", "gpt-image-2-5-flare", "seedream-5-pro", "z-image"],
"capabilities": ["supports-reference-image", "supports-aspect-ratio"],
"inputSchema": {
"fields": [
{ "key": "prompt", "type": "text", "required": true },
{ "key": "provider", "type": "select", "options": ["nano-banana-pro", "gpt-image-2"] },
{ "key": "aspectRatio", "type": "select" },
{ "key": "promptPrefix", "type": "text" },
{ "key": "promptSuffix", "type": "text" }
]
}
}
}
```

| Campo | Significado |
| --- | --- |
| `type` | O tipo do nó, que também é a rota: `POST /v1/<type>`. |
| `label`, `category`, `description` | Como o editor nomeia e agrupa o nó. |
| `outputType` | `text`, `image`, `video`, `audio`, `data` ou `none`. |
| `creditCost` | O custo do nó em créditos, ou a faixa de custo. É o preço cobrado por uma execução, o mesmo número do botão **Executar** do nó. No **Cortar vídeo** (Trim Video), no **Vídeo em loop** (Loop Video) e no **Combinar vídeos** (Combine Videos), ele é, em vez disso, o preço de um bloco de 5 segundos, e uma execução custa um certo número de blocos. Só no Nodaro Cloud: as edições sem créditos omitem o campo. |
| `providers` | Os IDs de modelo que o nó aceita em `provider`. |
| `capabilities` | Flags de recursos, como `supports-reference-image`. |
| `inputSchema.fields` | As configurações do nó, com o tipo, se são obrigatórias e as opções. |

Todo nó que recebe um prompt também lista `promptPrefix` e `promptSuffix`: texto adicionado antes e depois do prompt. Veja [Texto antes e depois do prompt](https://nodaro.ai/docs/concepts/prompt-pre-post-text). Nós com limites por modelo trazem campos extras:

| Campo | Significado |
| --- | --- |
| `maxDurationSec` | A duração máxima que o nó aceita. |
| `sparseProviders` | Modelos com poucas durações de segmento. Um valor entre elas é ajustado para a mais próxima. |
| `providerResolutions` | As resoluções de cada modelo, por exemplo `{ "minimax-h3": ["2K", "768P"] }`. |
| `providerResolutionWire` | O valor exato a enviar para cada resolução, por exemplo `768P`, e não `768p`, para o nível mais barato do MiniMax Hailuo 3. |
| `soundtrack` | No **Gerar vídeo Pro** (Generate Video Pro): o servidor aceita a entrada `soundtrack` de áudio original. |

O descritor cresce com o tempo, então ignore os campos que você não conhece. Os mesmos dados alimentam a [referência de nós](https://nodaro.ai/docs/nodes).

## Descobrir modelos
`GET /v1/models` retorna o catálogo de modelos, agrupado por tipo e por fabricante. Ele é público, fica em cache por 5 minutos e traz os mesmos dados que a ferramenta `list_models` do MCP retorna.

**curl**

```bash
curl -s "https://app.nodaro.ai/v1/models?kind=video&mode=i2v" | jq '.totalModels'
```

**TypeScript SDK**

```ts
const catalog = await client.models.list({ kind: 'video', mode: 'i2v' })
for (const section of catalog.sections)
for (const family of section.families)
for (const m of family.models) console.log(m.id)
```

**CLI**

```bash
nodaro models list --kind video --mode i2v
```

A resposta é `{ sections, recommendations, totalModels }`. Cada modelo traz os próprios recursos (`modes`, `features`, `aspectRatios`, `resolutions`, `durations`), o `pricing` em créditos por variante no Nodaro Cloud e `promptTips` curtas. Ele também traz `doctrineCovered`, que só é `true` quando existe um guia de prompts com fontes para a família do modelo. Os créditos de `pricing` são o preço cobrado por uma execução, o mesmo número do botão **Executar** no editor.

| Parâmetro | Valores | Filtra por |
| --- | --- | --- |
| `kind` | `image`, `video` ou `audio` | Um tipo de mídia |
| `mode` | Por exemplo, `t2i`, `i2v`, `t2v`, `tts`, `video-analysis` | Uma operação |
| `family` | Um fabricante, por exemplo `Google` ou `Bytedance` | Um fabricante |
| `featuredOnly` | `true` | Modelos em destaque |

As [páginas de modelos](https://nodaro.ai/docs/models) mostram o mesmo catálogo.

## Descobrir valores de seletores
Os seletores de Controles criativos têm catálogos públicos de IDs válidos, os valores que `direction` e `subject` aceitam:

| Método | Caminho | O que retorna |
| --- | --- | --- |
| `GET` | `/v1/picker-catalogs` | Todos os seletores: `nodeType`, `label`, `kind`, o campo ou os campos, `optionCount` e `imageCount`. |
| `GET` | `/v1/picker-catalogs/:nodeType` | As opções de um seletor. `?detail=full` adiciona a `description` e o `promptHint` de cada opção, `?category=` filtra um seletor de campo único, e `?field=` retorna um campo de um seletor de vários campos. |
| `GET` | `/v1/catalogs` | Todos os catálogos em uma chamada, como a implantação os organizou. `data` só aparece quando a implantação registrou pacotes de catálogo. |
| `POST` | `/v1/text-to-picker` | “AI Fill”: escolhe IDs para vários seletores a partir de uma descrição de cena em texto livre. Consome créditos. |

Toda opção traz `id`, `label` e `term` (a frase curta para usar em um prompt), além de `imageUrl` quando a opção tem imagem. Os catálogos também vêm como dados no pacote npm `@nodaro/prompts`. Leia [Catálogos de seletores](https://nodaro.ai/docs/developers/picker-catalogs) para ver os formatos completos. No terminal: `nodaro pickers list`, `nodaro pickers get mood --full` e `nodaro pickers analyze "<text>"`.

## Saída estruturada de LLM
`POST /v1/llm/structured` faz uma chamada a um modelo de linguagem cuja resposta é forçada a seguir um JSON Schema que você fornece, validada e retornada como objeto. A cobrança é em créditos, conforme o nível do modelo.

```json
{
"system": "You write production plans.",
"input": "A rainy chase through Rome.",
"jsonSchema": {
"type": "object",
"properties": { "title": { "type": "string" } },
"required": ["title"]
}
}
```

A resposta é `{ jobId, output, usage: { inputTokens, outputTokens } }`, em que `output` tem o formato do seu schema.

- **Campos:** `system`, `input`, `jsonSchema` e, opcionalmente, `schemaName` (até 64 caracteres), `llmModel`, `reasoningEffort`, `maxRetries`, `origin`, `advancedMode`, `temperature` e `maxTokens`. `system` e `input` aceitam até 100.000 caracteres cada, e `input` precisa de pelo menos um.
- **Modelo:** sem `llmModel`, a chamada roda no Gemini 3.6 Flash.
- **Schema:** a raiz precisa ser um schema de objeto simples, com até 64 KB e 20 níveis de profundidade. Ele pode usar `properties`, `required`, `additionalProperties`, `items`, os tipos básicos, `enum`, `const`, os limites numéricos e de comprimento, `multipleOf`, `exclusiveMinimum` e `description`. `anyOf` e `oneOf` só são aceitos abaixo da raiz. `not`, `if`, `then`, `else`, as palavras-chave `dependent`, `$ref` externo e combinadores na raiz retornam `400`. Um `anyOf` de ramos `required` abaixo da raiz é aceito, mas não é aplicado, então verifique você mesmo as regras que envolvem vários campos.
- **Novas tentativas:** `maxRetries`, de 0 a 3, com padrão 2, é quantas vezes uma resposta inválida volta ao modelo com o erro de validação.
- **Amostragem:** `maxTokens` vale em toda chamada e não pode passar do limite do próprio modelo. `temperature` é ignorado, a menos que você também envie `advancedMode: true`, que cobra um nível de crédito acima, com teto no premium.
- **Duração:** a chamada é síncrona e pode levar vários minutos. Cada tentativa pode levar até 240 segundos em cada uma das duas vias, então o pior caso é de 24 minutos com o `maxRetries` padrão e de 32 minutos com o máximo. Aumente o timeout do seu cliente HTTP ou use a forma de job abaixo. O `timeoutMs` padrão do SDK, de 60 segundos, é curto demais.
- **Erros:** `400 validation_error`, `401`, `402`, `500 internal_error`, `502 llm_error` depois que as novas tentativas se esgotam, e `503 provider_unavailable`.

No SDK, chame `client.llm.structured(body)`.

### Como job
`POST /v1/llm/structured/jobs` recebe o mesmo corpo e responde `{ jobId }` na hora. Consulte periodicamente `GET /v1/jobs/:id/status`: em `completed`, `output_data` é `{ output, inputTokens, outputTokens }`, e em `failed`, `error_message` diz o motivo. Encontre os seus rascunhos de novo com `GET /v1/jobs?type=llm-structured&origin=<your app>`. Três campos extras são aceitos:

- `label`: um nome de exibição para o job, com até 120 caracteres.
- `videoUrl`: gera o rascunho a partir de um vídeo. Primeiro o vídeo é analisado, em um job separado que também é seu, pelo preço da análise de vídeo, e a análise é adicionada ao seu `input`. Durante a execução, `output_data.stage` é `analyzing` e depois `drafting`.
- `videoAnalysis`: `{ llmModel?, selectionMode? }` para essa análise.

Cancelar o rascunho com `POST /v1/jobs/:id/cancel` também cancela uma análise em andamento. Uma análise recusada libera todos os créditos reservados para o rascunho. Em uma instalação que envia as chamadas de modelo de linguagem para o nodaro.ai, a forma de job retorna `503 provider_unavailable`: nesse caso, use a chamada síncrona. No SDK, chame `client.llm.structuredJob(body)`.

## Frequently asked questions

### Como gerar uma imagem com a API do Nodaro?

Envie POST /v1/generate-image com os campos prompt e provider. A resposta traz um jobId. Consulte periodicamente GET /v1/jobs/:id/status até o status ser completed e leia output_data.imageUrl. No SDK, client.nodes.runAndWait faz as duas etapas.

### Quais nós posso executar pela API?

Todos os tipos de nó que GET /v1/nodes lista. A rota é POST /v1/ seguido do tipo do nó, com as configurações do nó como corpo JSON. Alguns nós de texto usam um caminho mais longo, como /v1/llm-chat/generate.

### O que é o objeto direction?

Um mapa simples de IDs de seletores, como { "shotSize": "wide-shot", "timeOfDay": "golden-hour" }, que você envia com uma geração de imagem ou de vídeo. O Nodaro escreve no prompt a própria redação testada para cada ID. Os IDs válidos vêm de GET /v1/picker-catalogs.

### Como manter um personagem consistente pela API?

Envie a imagem do personagem em connectedReferences, com source wired-character e o nome do personagem em defaultName. A rota anexa a imagem e escreve as instruções de identidade no prompt, como o editor faz.

### Os endpoints de descoberta precisam de token?

Não. GET /v1/nodes, GET /v1/models, GET /v1/picker-catalogs e GET /v1/catalogs são públicos e ficam em cache por 5 minutos. Eles expõem apenas dados de catálogo, nunca nada sobre a sua conta.

### O que acontece quando o meu prompt com os IDs de direction fica longo demais para o modelo?

O Nodaro remove primeiro as cláusulas de direção, uma de cada vez a partir do final, até o prompt caber. As cláusulas de assunto são as últimas a sair, e o seu próprio texto e as suas referências nunca são removidos para dar lugar a elas.
