O formato de arquivo do flow (YAML / JSON)
Tudo o que está no canvas de um flow também é um arquivo de texto. Ver arquivo mostra o flow em YAML ou JSON, você pode editar ali e ver o canvas acompanhar, e o mesmo formato é o que um repositório Git conectado guarda como .flow.yaml. O arquivo guarda só o significado — passos, ligações e configurações — nunca posições: o canvas se organiza sozinho a partir da estrutura.
- Abrir, ler e editar um flow em YAML ou JSON
- Conhecer as chaves: trigger, a, b, c… e onError
- Escrever cada forma de vínculo e cada nó estrutural
- Copiar um flow entre flows, e guardá-lo no Git
Um flow é um arquivo
Abra o menu ⋯ do flow e escolha Ver arquivo (“Ler este flow como YAML”). Abre uma gaveta com a definição inteira e uma chave YAML / JSON. Mudanças no canvas aparecem no arquivo na hora; edições no arquivo chegam ao canvas cerca de meio segundo depois que você para de digitar, desde que o texto seja válido — senão a gaveta diz YAML parse error e o canvas mantém a última versão boa. As edições ficam não salvas até você clicar em Salvar, igual às edições no canvas.
Pílulas de ramo, finais de grupo, a junção abaixo de ramos em paralelo e o corpo sombreado do loop também não estão no arquivo. Eles são derivados da estrutura toda vez que o canvas é desenhado, e por isso um arquivo escrito à mão — ou por um assistente de IA, ou trazido do Git — aparece tão organizado quanto um montado arrastando.
Um exemplo completo
Um flow que carrega pedidos acima de um total mínimo, dá uma nota a cada um num loop em paralelo, gera um relatório quando houve pedidos, e tem um passo Em caso de erro — exatamente como o Ver arquivo escreve:
trigger:
parameters:
- name: minTotal
type: number
default: 100
next: a
a:
kind: script
label: Load orders
alias: loadOrders
lang: postgresql
code: |
-- @param min_total number = 100
SELECT id, email, total FROM orders WHERE total >= :min_total
integration: 9b1c6e2a-5d1f-4c1e-9a7e-2f0c3b8d4e61
inputs:
min_total:
from: trigger
path: minTotal
next: b
b:
kind: loop
label: Each order
alias: eachOrder
iterate:
from: a
path: rows
mode: parallel
concurrency: 5
body:
- c
next: d
c:
kind: script
label: Score
alias: score
lang: python
code: |
def main(total: float):
return {"vip": total > 500}
inputs:
total:
from: b
path: item.total
retry:
attempts: 2
delay: exponential
delaySec: 2
d:
kind: condition
label: Any orders?
alias: anyOrders
if: nodes.loadOrders.output.rowCount > 0
then:
label: "Yes"
nodes:
- e
else:
label: "No"
nodes: []
e:
kind: script
label: Report
alias: report
lang: typescript
code: |
export async function main() {
return { ok: true }
}
onError:
kind: script
label: Notify
alias: notify
lang: python
code: |
def main(message: str = "", step: str = ""):
print(f"{step} failed: {message}")triggeré sempre o nó Início; osparametersdele são as entradas do flow enextindica o primeiro passo.- Todo outro passo ganha uma chave curta —
a,b, …z,aa,ab— em ordem de largura a partir do Início. As chaves valem só dentro do arquivo e são refeitas toda vez que ele é escrito; o passo mantém o id real por trás, então execuções e vínculos continuam presos a ele. aliasé o nome usado nas expressões (nodes.loadOrders.output); ele é derivado do rótulo e aparece como referência.onError(epreprocessor) são entradas de primeiro nível semnext— elas não ficam na cadeia.
Tipos de nó e os campos deles
| kind | Campos |
|---|---|
script | lang (python, typescript, javascript, c, cpp, postgresql, mysql), code num passo próprio ou script (o caminho) num vinculado, integration e rowLimit em SQL, dependencies em Python próprio. |
condition | if, then: {label, nodes}, elseIfs: [{id, label, if, nodes}] opcional, else: {label, nodes}. nodes lista todo passo dentro daquele ramo. |
loop | iterate (um vínculo, uma lista ou uma expressão), mode: parallel + concurrency ou mode: while + maxIterations, breakIf, body (todo passo dentro). |
parallel | branches: [{id, name, mayFail?, nodes}], sequential: true opcional. |
flow | flowRef (o id do filho — a referência de verdade) e flowName (uma dica legível, nunca lida de volta). |
aiScraper | scraper (id), scraperName (dica), timeoutMs opcional. |
email | provider (ausente = o padrão do projeto), template; destinatários e valores são inputs (to + um por valor do template). |
approval | fields, approvers, timeoutSec, onTimeout, notify, allowLinks; a mensagem é inputs.message. Valores padrão são omitidos. |
c:
kind: parallel
label: Fan out
branches:
- id: b1x9k2
name: Send e-mail
nodes: [d]
- id: b7qq01
name: Notify team
mayFail: true
nodes: [e]
next: f
m:
kind: flow
label: Enrich customer
flowRef: 5f0e8a3c-2b7d-4e19-8c55-1a2b3c4d5e6f
flowName: Enrich customer
inputs:
customerId: { from: a, path: id }
retries: { manual: "3" }
apiKey: { var: CRM_API_KEY }
next: nQualquer passo executável também pode trazer as configurações de execução com os mesmos nomes do passo — retry, continueOnError, skipIf, stopIf, sleepAfterSec, cacheTtlSec, timeoutMs — e uma note curta. O que está desligado simplesmente não aparece, então um flow sem configurações não tem nenhuma dessas chaves.
Formas de vínculo
| Forma | Significa |
|---|---|
{ from: a, path: rows.0.total } | Um valor da saída do passo a, naquele caminho (sem path = a saída inteira). Dentro de um loop, from o loop com path: item.total lê o item atual. |
{ manual: "42" } | Uma constante, convertida para o tipo declarado do parâmetro. |
{ var: NAME } | Uma variável do projeto ou global, lida quando o passo roda. |
{ expr: "vars.RETRIES * 2" } | Uma expressão. O Ver arquivo hoje mostra isso como { manual: "__expr__:vars.RETRIES * 2" }; arquivos do Git e o servidor MCP escrevem expr. |
Copiar, exportar e guardar no Git
- Exportar
Abra o Ver arquivo, escolha YAML ou JSON e copie o texto. Esse texto é o flow inteiro; guarde, compartilhe, revise num pull request.
- Importar em outro flow
Crie um flow vazio, abra o Ver arquivo dele, troque o texto pela cópia e clique em Salvar. Os passos ganham ids novos; scripts vinculados, conexões e flows filhos são referenciados por id, então precisam existir no projeto de destino.
- Guarde no Git
Com o projeto conectado a um repositório (Git), cada flow é um arquivo
<pasta>/<nome>.flow.yamlno mesmo formato. Um commit grava o flow; um pull traz o arquivo como rascunho do flow — nunca publicado — para você revisar e publicar.
Arquivos do Git diferem do Ver arquivo em pequenos pontos: expressões são escritas { expr: … }, constantes mantêm o tipo JSON, rótulos padrão de ramo e alias ficam de fora, e o arquivo é validado ao ser lido — uma chave ou tipo desconhecido, um if ou iterate faltando, ou um passo que nenhum caminho alcança é apontado em vez de adivinhado.
O que o arquivo não guarda
- Nada de posições, cores ou zoom: o layout é sempre derivado.
- Nenhuma versão fixada num passo
flowouaiScraper: quem chama sempre roda a versão publicada mais recente. - Não as configurações do próprio flow — tempo limite, limite de concorrência, gatilhos e estado ficam fora da definição.