Workflows
Execute workflows do Nodaro pela API com novas entradas, aguarde ou consulte periodicamente o resultado; liste, crie, atualize, exporte e mova workflows.
Um workflow é um canvas salvo de nós conectados. A API pode executá-lo, passar novos valores de entrada para uma única execução e gerenciá-lo como qualquer outro recurso. Uma execução começa com uma requisição que retorna um executionId, e você consulta a execução periodicamente até ela terminar. Uma execução curta também pode aguardar o resultado na mesma requisição.
Endpoints
Executar um workflow
| Método | Caminho | O que faz |
|---|---|---|
POST | /v1/workflows/:id/run | Executa o workflow salvo, ou alguns dos nós dele. Responde 202 com um executionId. |
GET | /v1/api/workflows | Lista os workflows que o seu token de API pode executar. Aceita ?limit= e ?cursor=. |
GET | /v1/api/schema?workflowId=… | Os campos de entrada e as saídas de um workflow, com estimatedCredits. |
POST | /v1/api/run | Executa um workflow com novos valores de entrada. Adicione ?wait=true&timeout=… para aguardar o resultado. |
GET | /v1/api/status/:execId | O status da execução, as contagens de nós e os créditos usados. |
GET | /v1/api/result/:execId | As saídas da execução, quando o status dela for completed ou failed. |
POST | /v1/app/:slug/run | Executa um app publicado com os campos do formulário dele. Veja Miniapps. |
Gerenciar workflows
| Método | Caminho | O que faz |
|---|---|---|
GET | /v1/projects/:projectId/workflows | Os workflows de um projeto, sem os nós e as conexões. |
GET | /v1/workflows | Os seus workflows de todos os projetos. |
GET | /v1/workflows/:id | Um workflow com os nós, as conexões e as configurações dele. |
POST | /v1/projects/:projectId/workflows | Cria um workflow em um projeto. |
PATCH | /v1/workflows/:id | Altera qualquer subconjunto dos campos de um workflow. |
DELETE | /v1/workflows/:id | Exclui um workflow. |
GET | /v1/workflows/:id/export | Exporta o workflow como um pacote JSON. Adicione ?assets=true para incluir os personagens, objetos e locais dele. |
POST | /v1/workflows/import | Cria um workflow a partir de um pacote. |
POST | /v1/workflows/:id/move | Move um workflow para outro projeto. |
GET | /v1/workflows/shared-with-me | Os workflows que outras pessoas compartilharam com você. |
O compartilhamento, os colaboradores e as permissões por workflow estão em Espaços de trabalho e organizações.
Três formas de executar um workflow
| Endpoint | Credencial | Valores de entrada | Use quando |
|---|---|---|---|
POST /v1/workflows/:id/run | Qualquer token. Tokens OAuth precisam de workflows:execute. | Os valores salvos. nodeIds executa um subconjunto. | Você executa o workflow como ele está salvo, para a sua conta ou para um usuário OAuth. |
POST /v1/api/run | Um token de API pessoal | inputs substitui os valores dos nós de entrada nesta execução. | Um script precisa de valores diferentes a cada execução, ou quer aguardar o resultado. |
POST /v1/app/:slug/run | Qualquer token | Os campos do formulário do app, mais substituições diretas de campos de nós | O workflow está publicado como um app com um formulário com curadoria. |
Os cinco endpoints /v1/api/ são a via original dos tokens de API. Eles existem desde antes dos apps publicados e continuam com suporte, mas as novas integrações que precisam de entradas costumam publicar o workflow como app e usar POST /v1/app/:slug/run. Essa rota recebe os campos do app como um mapa plano em inputs e um objeto opcional inputOverrides, com substituições diretas no formato { nodeId: { field: value } }. Os dois são mesclados, e inputOverrides prevalece em qualquer campo que ambos definam.
Executar um workflow salvo
POST /v1/workflows/:id/run inicia uma execução do workflow como ele está salvo. Envie um corpo vazio para executar todos os nós, ou nodeIds para executar um subconjunto:
curl -s -X POST https://app.nodaro.ai/v1/workflows/8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f/run \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'{ "executionId": "3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b", "status": "pending" }const { executionId } = await client.workflows.run(workflowId)
// Or run only some nodes:
await client.workflows.run(workflowId, { nodeIds: ['text-prompt-1', 'generate-image-1'] })nodaro workflows run 8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f --watch
nodaro workflows run 8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f --node text-prompt-1 generate-image-1Prop
Type
A resposta é 202 Accepted com { executionId, status }, em que status é pending ou running. Consulte a execução periodicamente com GET /v1/workflow-executions/:id, descrito em Execuções. Com --watch, a CLI faz a consulta periódica por você e sai com o código 2 quando a execução falha e 130 quando ela é cancelada.
| Status | Código | Significado |
|---|---|---|
| 402 | insufficient_credits | Seus créditos não cobrem o custo da execução no pior caso. |
| 403 | forbidden | Você pode ver o workflow, mas não pode executá-lo. Em um espaço de trabalho, para executar é preciso ter acesso de edição e ser um membro ativo. |
| 404 | not_found | O workflow não existe, ou você não pode vê-lo. |
| 409 | already_running | O workflow já tem uma execução ativa. A resposta traz o executionId dessa execução. |
Executar um workflow com novos valores de entrada
POST /v1/api/run recebe um objeto inputs que substitui os valores dos nós de entrada somente nesta execução. A autenticação é feita com um token de API pessoal. As chaves de inputs são ids de nós ou, por conveniência, rótulos únicos de nós. Dentro de cada chave, informe o campo de entrada do nó, como text para um prompt de texto.
O exemplo abaixo executa um workflow com um nó Texto (Text), de id text-prompt-1, conectado a um nó Gerar imagem (Generate Image).
Encontrar os campos de entrada
GET /v1/api/schema lista as entradas do workflow, com o campo que cada uma recebe, as saídas e uma estimativa dos créditos que uma execução custa:
curl -s "https://app.nodaro.ai/v1/api/schema?workflowId=$WORKFLOW_ID" \
-H "Authorization: Bearer $NODARO_API_KEY"{
"workflowId": "8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f",
"name": "Sunset stills",
"estimatedCredits": 50,
"inputs": [
{ "nodeId": "text-prompt-1", "key": "text", "label": "Prompt", "type": "text" }
],
"outputs": [
{ "nodeId": "generate-image-1", "label": "Generate Image", "type": "image" }
]
}Iniciar a execução
curl -s -X POST https://app.nodaro.ai/v1/api/run \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workflowId": "8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f",
"inputs": {
"text-prompt-1": { "text": "a cat at sunset" }
}
}'{ "executionId": "3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b", "status": "pending" }Consultar periodicamente até a execução terminar
GET /v1/api/status/:execId retorna o status, as contagens de nós e os créditos usados até o momento. Consulte a execução periodicamente, a cada 2 a 5 segundos, até o status ser completed, failed, cancelled, timed_out ou discarded.
Buscar o resultado
GET /v1/api/result/:execId retorna as saídas de uma execução completed ou failed. Para uma execução que terminou como cancelled, timed_out ou discarded, leia o errorMessage da resposta de status: essas execuções não têm payload de resultado, e a rota de resultado continua respondendo 202.
{
"executionId": "3f9e2b1a-7c6d-4e5f-8a9b-0c1d2e3f4a5b",
"status": "completed",
"creditsUsed": 50,
"durationMs": 12450,
"errorMessage": null,
"outputs": [
{
"nodeId": "generate-image-1",
"label": "Generate Image",
"type": "image",
"url": "https://…/output.png"
}
]
}O fluxo completo em um único script, e as mesmas chamadas em TypeScript:
BASE="https://app.nodaro.ai"
WORKFLOW_ID="8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f"
EXEC=$(curl -s -X POST "$BASE/v1/api/run" \
-H "Authorization: Bearer $NODARO_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"workflowId\": \"$WORKFLOW_ID\", \"inputs\": {\"text-prompt-1\": {\"text\": \"a cat at sunset\"}}}" \
| jq -r .executionId)
while true; do
STATUS=$(curl -s -H "Authorization: Bearer $NODARO_API_KEY" \
"$BASE/v1/api/status/$EXEC" | jq -r .status)
echo "Status: $STATUS"
case "$STATUS" in completed|failed|cancelled|timed_out|discarded) break;; esac
sleep 5
done
curl -s -H "Authorization: Bearer $NODARO_API_KEY" "$BASE/v1/api/result/$EXEC" | jq .// The /v1/api/ lane has no dedicated SDK method: call it with client.request.
const schema = await client.request('GET', '/v1/api/schema', {
query: { workflowId },
})
const { executionId } = await client.request<{ executionId: string }>('POST', '/v1/api/run', {
body: { workflowId, inputs: { 'text-prompt-1': { text: 'a cat at sunset' } } },
})
const final = ['completed', 'failed', 'cancelled', 'timed_out', 'discarded']
let status = 'pending'
while (!final.includes(status)) {
await new Promise((r) => setTimeout(r, 3_000))
;({ status } = await client.request<{ status: string }>('GET', `/v1/api/status/${executionId}`))
}
const result = await client.request('GET', `/v1/api/result/${executionId}`)A CLI executa workflows somente com os valores salvos. Para passar valores pelo terminal, publique o workflow como app e execute nodaro apps run <slug> --input prompt="…".
Prop
Type
Um token com escopo de workflow só pode executar os workflows desse escopo. Qualquer outro workflow responde 403 forbidden. POST /v1/api/run e GET /v1/api/workflows contam para o limite por minuto do token; as leituras de status, de resultado e de schema não contam. Veja Limites de taxa.
Síncrono ou assíncrono
POST /v1/api/run é assíncrono por padrão: ele responde na hora 202 Accepted com { executionId, status: "pending" }, e você consulta a execução periodicamente.
Para um workflow curto, mantenha a conexão aberta até ele terminar:
POST /v1/api/run?wait=true&timeout=120- O servidor verifica a execução a cada 5 segundos por até
timeoutsegundos. O padrão é 120, e o máximo é 600. - Se a execução terminar a tempo, a resposta é o mesmo payload de
GET /v1/api/result/:execId. Ostatusdela écompleted,failed,cancelled,timed_outoudiscarded. - Se não terminar, a resposta é
202com{ executionId, status: "pending" }, e você passa a consultar periodicamente a partir daí.
Use a forma síncrona para execuções que devem terminar em menos de um minuto, como geração de texto e trabalhos leves de imagem. Use a forma assíncrona para workflows que renderizam vídeo ou fazem upscale de vídeo. No SDK, aumente o timeoutMs de createClient para um valor acima do seu timeout, porque o cliente desiste depois de 60 segundos por padrão.
POST /v1/workflows/:id/run e as rotas de geração são sempre assíncronos. Para um único nó, o nodes.runAndWait do SDK consulta o job periodicamente por você: veja Executar um único nó.
O que uma execução não pode mudar
Uma requisição de execução não pode redirecionar os nós externos (outbound) de um workflow. Para onde um workflow envia dados e de onde ele os busca é decidido pelo próprio workflow, em todos os endpoints de execução, incluindo POST /v1/workflows/:id/run, POST /v1/api/run e POST /v1/app/:slug/run.
Os nós externos são Saída de webhook (Webhook Output) e os nós de publicação em redes sociais, como Publicar nas redes (Publish to Social). Também são externos os nós de busca, como Extrair da web (Web Scrape), Feed de canal do Telegram (Telegram Channel Feed) e URL de vídeo (Video URL). Nesses nós, uma substituição não pode alterar:
- campos cujo nome termina em
UrlouUrls; target,targets,query,channel,chatId,connectionId,credentialId,platform,webhook,endpoint,hostouprivacy;- os seletores
actoremode, que escolhem qual campo de destino o nó de busca lê.
A regra vale para valores aninhados em um objeto ou em uma entrada de fieldMappings. Ela vale tanto para um valor vazio quanto para um novo endereço: apagar um destino faria o nó ler, no lugar dele, o texto que vem dos nós anteriores. Uma requisição assim recebe 400 locked_field antes de qualquer coisa ser executada. O erro nomeia até dez dos campos problemáticos e informa quantos são os demais. Uma substituição aninhada a mais de 32 níveis de profundidade em um nó desses é recusada de imediato.
Os campos comuns desses nós, como uma legenda ou um limite, e os campos de mídia url dos nós de entrada, como uploads e áudio de referência, continuam podendo ser substituídos.
Correções de parâmetros
As rotas de geração de imagem e de vídeo aceitam um único vocabulário para aspectRatio, resolution e quality, qualquer que seja o modelo. Nenhum modelo aceita todos esses valores. Em vez de rejeitar um valor que o modelo escolhido não aceita, o servidor o corrige para um valor que o modelo aceita e informa o que mudou. Uma rejeição no meio de um workflow faria falhar todos os nós ao lado dele que já tivessem gerado resultados e sido cobrados.
As rotas que corrigem valores são POST /v1/generate-image, /v1/image-to-image, /v1/edit-image, /v1/text-to-video e /v1/generate-video:
{
"jobId": "0f1a9c2e-5b7d-4e8a-9c31-6d2f8b4a7e10",
"adjustments": [
{
"field": "aspectRatio",
"from": "3:2",
"to": "auto",
"reason": "GPT Image 2 does not support aspectRatio \"3:2\" — using \"auto\" instead. Supported: auto, 1:1, 16:9, 9:16, 4:3, 3:4."
},
{
"field": "resolution",
"from": "4K",
"to": "1K",
"reason": "GPT Image 2 only renders 1K at the \"auto\" aspect ratio."
}
]
}adjustmentsnão aparece quando nada mudou. A resposta de uma requisição válida é exatamente{ "jobId": "…" }.tonão aparece quando o modelo não tem essa configuração e o valor foi descartado, por exemplo umaspectRatioenviado a um upscaler.- Os créditos seguem o valor corrigido. Uma requisição ao GPT Image 2 com
autoem2Ké renderizada e cobrada em 1K, porqueautosó renderiza em 1K. - Os workflows salvos também são corrigidos. Um workflow salvo pela API ou pelo MCP recebe as mesmas correções quando é gravado.
- Os valores aceitos por cada modelo estão em
GET /v1/models. Veja Descobrir modelos.
Correções nas rotas de vídeo
/v1/text-to-video e /v1/generate-video retornam adjustments no mesmo formato. /v1/generate-video também repete o reason de cada correção no array warnings, junto com avisos que não são sobre parâmetros, como voice_unsupported_for_provider. Leia adjustments quando precisar saber qual configuração mudou. Os dois nunca divergem.
- Um valor não aceito vai para a opção mais próxima, nunca para a mais barata nem para a primeira. Uma requisição
4kem um modelo que vai até 1080p renderiza em 1080p, não em 480p, e um9:21vertical vira9:16, não16:9. - Uma
resolutionomitida é enviada na faixa em que é cobrada. Quando a plataforma declara a faixa padrão de um modelo, você é cobrado por essa faixa, e é essa faixa que o modelo recebe. O valor aparece noinput_datado job, então você sempre pode ver o que foi enviado. - A grafia é normalizada antes do cálculo do preço.
4Ké lido como4k, então é cobrado e renderizado na faixa 4K. - Alguns modelos renderizam uma faixa fixa para qualquer outro valor. O MiniMax Hailuo 3 renderiza em 2K tudo o que não for
768P, e a família Wan 3.0 renderiza em 720p. Nesses casos, a correção aponta para a faixa que o modelo vai produzir, então o preço corresponde à renderização. A entrada do modelo emGET /v1/modelsdeclara isso emunlistedResolutionRendersAs. durationé enviado como você informar, exceto nos modelos LTX 2.3. Eles são cobrados por uma escala fixa de durações por resolução, então uma duração entre dois degraus vai para o degrau mais próximo e é informada emadjustments.duration: -1significa Automática nos modelos que oferecem essa opção, a família Seedance 2 (autoDuration: trueemGET /v1/models). O modelo escolhe a duração do clipe, que é a duração do clipe de origem quando ele edita um vídeo de referência. Uma execução com duração Automática reserva créditos para o clipe mais longo do modelo e reembolsa a diferença até a duração entregue. Os outros modelos ignoram-1e renderizam na duração padrão.
As rotas de imagem de personagem e de local (/v1/generate-character, /v1/generate-character-asset, /v1/generate-location e /v1/generate-location-asset) corrigem quality e resolution da mesma forma, mas não retornam adjustments. O valor corrigido só fica visível no input_data do job, em GET /v1/jobs/:id.
Gerenciar workflows
Listar e ler
GET /v1/projects/:projectId/workflows retorna os workflows de um projeto sem nodes, edges e settings. GET /v1/workflows/:id retorna um workflow completo. Em uma organização, a lista segue o espaço de trabalho em que você atua: veja Espaços de trabalho.
curl -s https://app.nodaro.ai/v1/projects/$PROJECT_ID/workflows \
-H "Authorization: Bearer $NODARO_API_KEY" | jq '.data[] | {id, name}'const { data: workflows } = await client.workflows.list({ projectId })
const { data: workflow } = await client.workflows.get(workflows[0].id)
console.log(workflow.nodes.length)nodaro workflows list --project $PROJECT_ID --json
nodaro workflows get 8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2fCriar e atualizar
Crie um workflow em um projeto com POST /v1/projects/:projectId/workflows. Tudo, exceto o projeto, é opcional e usa os padrões do servidor quando omitido:
const { data: wf } = await client.workflows.create({
projectId,
name: 'My workflow',
nodes: [],
edges: [],
})PATCH /v1/workflows/:id altera qualquer subconjunto de campos. Para proteger uma atualização contra uma edição simultânea, envie expectedVersion, o inteiro version da sua última leitura. Se o workflow mudou desde então, a atualização é recusada com 409 workflow_conflict, e o erro traz currentVersion, currentUpdatedAt e currentRecord, que é o workflow atual completo. Mescle sua alteração em currentRecord e salve de novo, sem uma leitura extra. expectedUpdatedAt, um timestamp, funciona do mesmo jeito.
import { WorkflowConflictError } from '@nodaro/sdk'
try {
await client.workflows.update(id, { name: 'Renamed', expectedVersion: 7 })
} catch (err) {
if (err instanceof WorkflowConflictError && err.currentRecord) {
await client.workflows.update(id, { name: 'Renamed', expectedVersion: err.currentVersion })
} else throw err
}- Os valores de estado de execução no
datade um nó, como o status de execução, o job atual e o progresso, são removidos ao salvar e nunca são armazenados. thumbnailUrldefine a imagem de prévia do workflow a partir de uma imagem já hospedada.nullremove a imagem.- Um salvamento que envia só
edgese conecta uma camada de Sobreposição em vídeo (Video Overlay) reescreve esse nó. Por isso, ele responde409 workflow_conflictquando o workflow mudou desde a leitura, mesmo semexpectedVersion. visibilityéprivateouworkspace. Só o criador ou um administrador do espaço de trabalho pode alterá-la, e um workflow fora de um espaço de trabalho responde400 not_workspace_scoped.
Excluir
DELETE /v1/workflows/:id exclui um workflow. O criador e um administrador do espaço de trabalho podem excluí-lo; um colaborador, nunca. Excluir um workflow que não existe, ou que você não pode ver, responde 404, então uma exclusão nunca é um sucesso silencioso. Um colaborador que pode ver o workflow, mas não pode excluí-lo, recebe 403.
Exportar e importar
GET /v1/workflows/:id/export retorna o workflow como um pacote JSON portátil. Com ?assets=true, o pacote também leva os personagens, objetos e locais que o workflow usa, se forem seus. Quando os nós apontam para mídias que outra instalação não consegue buscar, como arquivos em localhost ou em uma rede privada, o pacote as lista em portability.unreachableMedia.
POST /v1/workflows/import cria um workflow a partir de um pacote:
POST /v1/workflows/import
{ "projectId": "<project uuid>", "workflow_json": { "version": 1, "name": "…", "nodes": [], "edges": [] } }A importação recria na sua conta os personagens, objetos, criaturas e locais do pacote e faz os nós apontarem para eles. Ela copia as mídias acessíveis para o armazenamento desta instalação: até 25 arquivos para as mídias do workflow e mais 25 para as entidades do pacote, com imagens de até 20 MB e vídeo ou áudio de até 50 MB. A resposta traz o novo workflow e um importReport:
| Campo | Significado |
|---|---|
rehosted | Quantos arquivos foram copiados para esta instalação. |
unreachable | Mídias em hosts privados, que continuam apontando para onde estavam. Esses nós não são executados até o arquivo ser enviado de novo. |
skipped | Mídias que não puderam ser copiadas, com o motivo, como HTTP 404. |
assetIdMap | Cada id de entidade do pacote, mapeado para o registro criado para ela. |
assetsSkipped | Entidades que não couberam na sua cota de armazenamento. O workflow é criado mesmo assim. |
Pela CLI: nodaro workflows export <id> --with-assets --output bundle.json e depois nodaro workflows import bundle.json --project <projectId>. Leia Importação e exportação para saber o que é transferido.
Mover para outro projeto
POST /v1/workflows/:id/move
{ "projectId": "…" }Mover é uma gravação no workflow, então um token OAuth precisa de workflows:write. PATCH /v1/workflows/:id com um projectId faz a mesma coisa e segue as mesmas regras. Você pode mover o trabalho que criou. Dentro de uma organização, um administrador de espaço de trabalho também pode mover trabalho entre dois espaços de trabalho que administra. Um projeto pessoal precisa ser seu nos dois lados.
| Status | Código | Significado |
|---|---|---|
| 400 | validation_error | O workflow já está nesse projeto. |
| 403 | not_permitted | Você não pode mover esse workflow, ou não pode movê-lo para esse destino. |
| 404 | not_found | O workflow não existe, ou o projeto não existe para você. |
| 409 | move_blocked | O trabalho foi criado para uma atividade. |
| 409 | workspace_archived | O espaço de trabalho de destino está arquivado. |
Uma movimentação que muda de espaço de trabalho remove as concessões de acesso dos colaboradores do workflow e as informa, para que você possa avisar as pessoas que perderam o acesso:
{
"data": { "id": "8c2d7f1e-3a4b-4c5d-9e6f-7a8b9c0d1e2f", "projectId": "5e4d3c2b-1a09-4f8e-8d7c-6b5a49382716" },
"droppedCollaborators": [{ "userId": "2b3c4d5e-6f70-4a81-92b3-c4d5e6f70812", "name": "Sam" }]
}A forma com PATCH só inclui droppedCollaborators quando alguma concessão foi removida.
Escopos OAuth para workflows
| Escopo | Rotas |
|---|---|
workflows:read | GET /v1/projects/:projectId/workflows, GET /v1/workflows, GET /v1/workflows/:id, GET /v1/workflows/:id/export |
workflows:write | Criar, atualizar, excluir, importar e mover, além de POST /v1/workflows/:parentId/sub-workflows |
workflows:execute | POST /v1/workflows/:id/run |
Tokens de API pessoais não precisam de escopos. Veja Apps OAuth.
Nós de produções do Studio
Alguns nós do canvas pertencem a uma produção do Studio e dependem de quadros vinculados. Eles precisam ser gerados pela API de produções do Studio: executá-los por POST /v1/workflows/:id/run, ou gerar um deles diretamente, responde 400 sequence_execution_required. Veja Produções do Studio.
Perguntas frequentes
Páginas relacionadas
Execuções
Nós
Incorporar um miniapp
Execução de workflows
Webhooks
Última atualização
Autenticação
Autentique chamadas à API do Nodaro com token de API pessoal, token de app OAuth ou JWT de sessão, e crie, limite, vincule e revogue os seus tokens de API.
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.