Docs do Nodaro
DocumentaçãoReferência de nósModelosAgentes de IA (MCP)DesenvolvedoresSelf-hostingPesquisa
API REST

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étodoCaminhoO que faz
POST/v1/workflows/:id/runExecuta o workflow salvo, ou alguns dos nós dele. Responde 202 com um executionId.
GET/v1/api/workflowsLista 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/runExecuta um workflow com novos valores de entrada. Adicione ?wait=true&timeout=… para aguardar o resultado.
GET/v1/api/status/:execIdO status da execução, as contagens de nós e os créditos usados.
GET/v1/api/result/:execIdAs saídas da execução, quando o status dela for completed ou failed.
POST/v1/app/:slug/runExecuta um app publicado com os campos do formulário dele. Veja Miniapps.

Gerenciar workflows

MétodoCaminhoO que faz
GET/v1/projects/:projectId/workflowsOs workflows de um projeto, sem os nós e as conexões.
GET/v1/workflowsOs seus workflows de todos os projetos.
GET/v1/workflows/:idUm workflow com os nós, as conexões e as configurações dele.
POST/v1/projects/:projectId/workflowsCria um workflow em um projeto.
PATCH/v1/workflows/:idAltera qualquer subconjunto dos campos de um workflow.
DELETE/v1/workflows/:idExclui um workflow.
GET/v1/workflows/:id/exportExporta o workflow como um pacote JSON. Adicione ?assets=true para incluir os personagens, objetos e locais dele.
POST/v1/workflows/importCria um workflow a partir de um pacote.
POST/v1/workflows/:id/moveMove um workflow para outro projeto.
GET/v1/workflows/shared-with-meOs 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

EndpointCredencialValores de entradaUse quando
POST /v1/workflows/:id/runQualquer 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/runUm token de API pessoalinputs 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/runQualquer tokenOs campos do formulário do app, mais substituições diretas de campos de nósO 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-1

Prop

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.

StatusCódigoSignificado
402insufficient_creditsSeus créditos não cobrem o custo da execução no pior caso.
403forbiddenVocê 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.
404not_foundO workflow não existe, ou você não pode vê-lo.
409already_runningO 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.

promptTextotext-prompt-1Gerar imagemgenerate-image-1
O workflow de exemplo: um nó Texto, cujo texto a execução pela API substitui, alimenta um nó Gerar imagem.

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é timeout segundos. 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. O status dela é completed, failed, cancelled, timed_out ou discarded.
  • Se não terminar, a resposta é 202 com { 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 Url ou Urls;
  • target, targets, query, channel, chatId, connectionId, credentialId, platform, webhook, endpoint, host ou privacy;
  • os seletores actor e mode, 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."
    }
  ]
}
  • adjustments não aparece quando nada mudou. A resposta de uma requisição válida é exatamente { "jobId": "…" }.
  • to não aparece quando o modelo não tem essa configuração e o valor foi descartado, por exemplo um aspectRatio enviado a um upscaler.
  • Os créditos seguem o valor corrigido. Uma requisição ao GPT Image 2 com auto em 2K é renderizada e cobrada em 1K, porque auto só 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 4k em um modelo que vai até 1080p renderiza em 1080p, não em 480p, e um 9:21 vertical vira 9:16, não 16:9.
  • Uma resolution omitida é 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 no input_data do job, então você sempre pode ver o que foi enviado.
  • A grafia é normalizada antes do cálculo do preço. 4K é lido como 4k, 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 em GET /v1/models declara isso em unlistedResolutionRendersAs.
  • 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 em adjustments.
  • duration: -1 significa Automática nos modelos que oferecem essa opção, a família Seedance 2 (autoDuration: true em GET /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 -1 e 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-7a8b9c0d1e2f

Criar 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 data de um nó, como o status de execução, o job atual e o progresso, são removidos ao salvar e nunca são armazenados.
  • thumbnailUrl define a imagem de prévia do workflow a partir de uma imagem já hospedada. null remove a imagem.
  • Um salvamento que envia só edges e conecta uma camada de Sobreposição em vídeo (Video Overlay) reescreve esse nó. Por isso, ele responde 409 workflow_conflict quando o workflow mudou desde a leitura, mesmo sem expectedVersion.
  • visibility é private ou workspace. Só o criador ou um administrador do espaço de trabalho pode alterá-la, e um workflow fora de um espaço de trabalho responde 400 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:

CampoSignificado
rehostedQuantos arquivos foram copiados para esta instalação.
unreachableMí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.
skippedMídias que não puderam ser copiadas, com o motivo, como HTTP 404.
assetIdMapCada id de entidade do pacote, mapeado para o registro criado para ela.
assetsSkippedEntidades 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.

StatusCódigoSignificado
400validation_errorO workflow já está nesse projeto.
403not_permittedVocê não pode mover esse workflow, ou não pode movê-lo para esse destino.
404not_foundO workflow não existe, ou o projeto não existe para você.
409move_blockedO trabalho foi criado para uma atividade.
409workspace_archivedO 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

EscopoRotas
workflows:readGET /v1/projects/:projectId/workflows, GET /v1/workflows, GET /v1/workflows/:id, GET /v1/workflows/:id/export
workflows:writeCriar, atualizar, excluir, importar e mover, além de POST /v1/workflows/:parentId/sub-workflows
workflows:executePOST /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

Última atualização

Nesta página