Como os workflows funcionam
Um agente termina sua execução respondendo com JSON. Quando essa resposta traz a chave workflow, a plataforma a executa: uma cadeia de requisições HTTP, em ordem, contra os seus sistemas.
É isso que transforma "o agente leu a nota" em "o pagamento existe no ERP" — sem você ficar consultando nossa API nem escrever um serviço que escuta webhooks só para chamar você mesmo de volta.
Onde entra no pipeline
SES ──▶ Email (received)
│
├─ filtros ──▶ rejected
│
▼
approved ──▶ AgentRun (chamada ao LLM) ──▶ Email processed
│ │
│ resposta = { result, workflow }
│ │
└──────────────────▶ WorkflowRun ──▶ step 0 ──▶ step 1 ──▶ …
│
workflow.run.succeeded | failed
Duas coisas decorrem dessa posição, e elas são o desenho inteiro:
- O workflow roda depois que a análise já está registrada. O
AgentRunestásuccesse oEmailestáprocessedantes de a primeira requisição sair. Um destino fora do ar é falha do workflow, nunca do agente — você não perde a análise que já pagou ao modelo. - O workflow é um job separado. Tem registro próprio (
WorkflowRun), status próprio, eventos próprios. Nada nele bloqueia o recebimento de e-mails.
O formato da resposta
{
"result": { "supplier": "ACME", "amount": 1250.5, "due_date": "2026-10-01" },
"workflow": [
{
"type": "request",
"method": "POST",
"url": "https://erp.example.com/payables",
"headers": { "Authorization": "Bearer ${{secrets.FINANCE_TOKEN}}" },
"body": { "supplier": "ACME", "amount": 1250.5, "email_id": "${{kobana.email_id}}" },
"expected_status": 201
},
{
"type": "request",
"method": "POST",
"url": "https://erp.example.com/payables/${{steps.0.response.body.data.id}}/attachments",
"headers": { "Authorization": "Bearer ${{secrets.FINANCE_TOKEN}}" },
"form": { "file": "${{email.attachments.0}}" }
}
]
}
result mantém o significado de sempre e continua guardado em Email.agent_metadata. workflow é opcional — um agente que não devolve workflow se comporta exatamente como antes.
Veja Steps e placeholders para todos os campos.
Modelo de execução
Sequencial. Os passos rodam na ordem do array, um por vez. O passo 1 só começa depois que o passo 0 terminou.
Uma falha fica isolada. Todos os passos são tentados, em ordem. Um passo que falha fica failed sozinho; os seguintes continuam rodando. A exceção é um passo que referencia (${{steps.N.…}}) um passo que não teve sucesso — falhou ou foi pulado: ele fica skipped, nunca é enviado, e seu error diz depends on step N, which did not succeed. "Anexe o documento ao pagamento recém-criado" é errado de enviar quando o pagamento nunca foi criado, então é pulado; um segundo pagamento, independente, no mesmo workflow continua sendo criado. A execução fica failed se algum passo falhou. Coloque primeiro os passos independentes que você quer ver acontecer mesmo se outros falharem — um passo dependente só roda quando o que ele usa deu certo.
Exatamente uma vez. Não há retentativa automática, nem por passo nem pela execução inteira. Todo passo é uma requisição arbitrária aos seus sistemas; repeti-la poderia duplicar um pagamento, uma notificação ou uma cobrança. Uma execução que morre no meio (restart de worker) permanece running e é exibida assim, em vez de ser reexecutada às cegas — reexecutar é decisão humana, feita com POST /v1/emails/{id}/reprocess.
Um passo falha quando qualquer uma destas é verdadeira:
| Condição | error gravado |
|---|---|
Status não corresponde a expected_status (ou não é 2xx, quando ele está ausente) | expected status 201, got 422 |
| O destino não respondeu em 30s | timeout after 30000ms |
| Falha de conexão, de DNS ou de TLS | ECONNREFUSED …, ENOTFOUND … |
| O destino respondeu um redirecionamento | expected status 2xx, got 302 — 3xx nunca é seguido |
| Um placeholder não pôde ser resolvido | secret `X` is not defined in this workspace |
| A URL resolvida não é permitida | url not allowed: private_ip |
O que a plataforma garante
- Credenciais nunca chegam ao modelo. O agente escreve
${{secrets.FINANCE_TOKEN}}; o valor é substituído na hora do envio, a partir do cofre de Secrets. - Credenciais nunca chegam ao banco. A requisição persistida em cada passo tem todo valor de secret trocado por
[REDACTED]. O que você lê emGET /v1/workflow-runs/{id}não é o texto original, e não existe endpoint que o devolva. - A URL é checada depois da substituição. Só
https://(fora de desenvolvimento), e endereços privados, de loopback, link-local e de metadados de nuvem são recusados — então um secret com uma URL base não serve para apontar um passo para uma rede interna. - A tentativa inteira é registrada. Requisição como enviada, status da resposta, corpo da resposta (até 4000 caracteres), duração e o erro. Suficiente para diagnosticar sem reexecutar.
- Um workflow malformado é barulhento. Se o agente emite algo que não valida, um
WorkflowRuné gravado comofailedcom o motivo (invalid workflow at `0.url`: …). Um agente que quis disparar três requisições nunca pode parecer um agente que quis disparar nenhuma.
Limites
| Limite | Valor |
|---|---|
| Passos por workflow | 20 |
| Timeout por passo | 30s |
| Resposta lida por passo | 1 MiB |
| Corpo de requisição/resposta guardado | 4000 caracteres |
| Retentativas | nenhuma |
Próximos passos
- Começando — montar um do começo ao fim.
- Steps e placeholders — a referência completa de campos e placeholders.
- Diagnóstico — o que cada falha significa e o que fazer.