Arazzo 1.1: o que há de novo e o que roda hoje
Em 19 de maio de 2026 a OpenAPI Initiative lançou o Arazzo 1.1.0. Arazzo é o formato da OpenAPI Initiative para descrever uma sequência de chamadas de API: fazer login, depois criar um pedido, depois pagar, com cada passo passando valores para o próximo. O OpenAPI diz o que um endpoint aceita e devolve. O Arazzo diz quais endpoints chamar, em que ordem, e como saber se cada chamada deu certo.
A versão 1.1 é a primeira com novidades desde a 1.0, de 2024. Escrevemos um workflow 1.1 contra a nossa própria API e rodamos com o Redocly CLI 2.62.1, lançado em 9 de outubro. O runner aceita documentos 1.1, e um workflow simples de criar, ler, atualizar e apagar passa. Duas das novidades passam no linter mas são ignoradas quando o workflow roda de verdade. Este post mostra o que mudou, traz um workflow que você pode baixar e rodar, e aponta as duas novidades que é melhor evitar por enquanto.
O que um arquivo Arazzo faz
Um documento Arazzo aponta para uma ou mais descrições de API, normalmente arquivos OpenAPI, e lista workflows. Cada workflow é uma lista de passos. Um passo nomeia uma operação pelo seu operationId, preenche parâmetros e um corpo de requisição, diz o que conta como sucesso e guarda valores da resposta para os passos seguintes.
Esse único arquivo é útil em três lugares:
- Testes. Um runner executa os passos contra um servidor real e falha se um critério de sucesso não for atendido. É assim que usamos abaixo.
- Documentação. Quem lê vê a ordem de chamadas que uma tarefa exige, algo que um arquivo OpenAPI não consegue expressar.
- Agentes de IA. Um agente que lê OpenAPI sabe o que cada endpoint faz, mas precisa adivinhar como combiná-los. Um workflow Arazzo diz a ele.
O que mudou no Arazzo 1.1
O destaque é o suporte a AsyncAPI, para que um workflow misture chamadas HTTP com canais de mensagens. Quase todo o resto deixa os workflows HTTP mais fáceis de escrever. Está tudo no anúncio da versão:
| Mudança | O que faz |
|---|---|
Fontes type: asyncapi | Uma descrição de origem agora pode ser um documento AsyncAPI, além de openapi e arazzo. |
channelPath, action, correlationId, timeout | Passos que enviam ou recebem mensagens. action é send ou receive, correlationId liga uma resposta à sua requisição, e timeout é um limite em milissegundos. |
dependsOn | Um passo pode listar outros passos que precisam terminar antes de ele começar. Listar um passo não o executa. |
parameters em ações | Uma ação de sucesso ou falha que chama outro workflow pode passar entradas para ele, por exemplo para renovar um token e depois tentar de novo. |
| Selector Object | Extrai um valor de uma resposta ou entrada com JSONPath, XPath ou JSON Pointer. Funciona em outputs, valores de parâmetros e corpos de requisição. |
| Expression Type Object | Fixa a versão de uma linguagem de consulta, como JSONPath conforme a RFC 9535 ou XPath 3.1. Substitui o Criterion Expression Type Object da 1.0. |
in: querystring | Envia um parâmetro como a query string inteira, igual à mesma novidade do OpenAPI 3.2. |
$self | Uma URI que identifica o documento, para que as referências a ele não dependam de onde o arquivo está guardado. |
A especificação também ganhou uma gramática completa para expressões de runtime como $steps.login.outputs.token, uma ordem definida para resolver as descrições de origem e regras exatas sobre quando uma condição conta como verdadeira. Elas não acrescentam campos, mas resolvem casos em que dois runners poderiam discordar. Suporte a gRPC, GraphQL, MCP e loops está no roadmap de versões futuras e não está na 1.1.
Um workflow que você pode rodar contra a nossa API
Este workflow cria um produto em api.testingapis.com, lê de volta, muda o preço, apaga e verifica se sumiu. Ele usa só endpoints gratuitos, então roda sem conta nem chave de API.
arazzo: 1.1.0
$self: https://testingapis.com/downloads/arazzo/products.arazzo.yaml
info:
title: Product lifecycle on testingapis.com
summary: Create a product, read it back, change its price, delete it and check that it's gone.
version: 1.0.0
sourceDescriptions:
- name: testingapis
url: ./testingapis-products.openapi.json
type: openapi
workflows:
- workflowId: product-lifecycle
inputs:
type: object
properties:
title: { type: string, default: Arazzo test product }
price: { type: number, default: 12.5 }
newPrice: { type: number, default: 9.99 }
steps:
- stepId: createProduct
operationId: Products_Create
requestBody:
contentType: application/json
payload:
title: $inputs.title
price: $inputs.price
category: electronics
successCriteria:
- condition: $statusCode == 201
outputs:
productId: $response.body#/data/id
- stepId: readProduct
operationId: Products_GetById
parameters:
- name: id
in: path
value: $steps.createProduct.outputs.productId
successCriteria:
- condition: $statusCode == 200
- context: $response.body
condition: $[?@.category == 'electronics' && @.inStock == false]
type:
type: jsonpath
version: rfc9535
onFailure:
- name: cleanUp
type: goto
stepId: deleteProduct
- stepId: changePrice
operationId: Products_Update
parameters:
- name: id
in: path
value: $steps.createProduct.outputs.productId
requestBody:
contentType: application/json
payload:
title: $inputs.title
price: $inputs.newPrice
category: electronics
successCriteria:
- condition: $statusCode == 200
- condition: $response.body#/data/price == $inputs.newPrice
onFailure:
- name: cleanUp
type: goto
stepId: deleteProduct
- stepId: deleteProduct
operationId: Products_Delete
parameters:
- name: id
in: path
value: $steps.createProduct.outputs.productId
successCriteria:
- condition: $statusCode == 200
- stepId: confirmGone
operationId: Products_GetById
parameters:
- name: id
in: path
value: $steps.createProduct.outputs.productId
successCriteria:
- condition: $statusCode == 404
outputs:
productId: $steps.createProduct.outputs.productIdBaixe junto com o arquivo OpenAPI para o qual ele aponta, coloque os dois na mesma pasta e rode. Node 22.12 ou mais recente basta, porque o npx baixa o runner:
- products.arazzo.yaml
- testingapis-products.openapi.json (nosso documento OpenAPI ao vivo, reduzido aos endpoints de produtos e echo, com uma entrada
serverspara o runner saber para onde mandar as requisições)
$ npx @redocly/cli@2.62.1 respect products.arazzo.yamlRunning workflow products.arazzo.yaml / product-lifecycle
✓ POST /api/Products - step createProduct
✓ success criteria check - $statusCode == 201
✓ status code check - $statusCode in [200, 201, 400, 500]
✓ GET /api/Products/{id} - step readProduct
✓ success criteria check - $statusCode == 200
✓ success criteria check - $[?@.category == 'electronics' && @.inStock == fal...
✓ status code check - $statusCode in [200, 404, 500]
✓ PUT /api/Products/{id} - step changePrice
✓ success criteria check - $statusCode == 200
✓ success criteria check - $response.body#/data/price == $inputs.newPrice
✓ status code check - $statusCode in [200, 404, 500]
✓ DELETE /api/Products/{id} - step deleteProduct
✓ success criteria check - $statusCode == 200
✓ status code check - $statusCode in [200, 404, 500]
✓ GET /api/Products/{id} - step confirmGone
✓ success criteria check - $statusCode == 404
✓ status code check - $statusCode in [200, 404, 500]
Workflows: 1 passed, 1 total
Steps: 5 passed, 5 total
Checks: 12 passed, 12 total
Time: 922msAlgumas partes do arquivo fazem mais do que parece:
$response.body#/data/idé uma expressão de runtime com um JSON Pointer depois do#. Nosso endpoint de criação embrulha o produto novo em{ message, data }, então o ID fica em/data/id. Todo passo seguinte lê o valor como$steps.createProduct.outputs.productId.- O critério em
readProductusa o novo Expression Type Object para dizer que a consulta é JSONPath da RFC 9535. Uma condição JSONPath passa quando o filtro encontra pelo menos um nó, então$[?@.category == 'electronics' && @.inStock == false]verifica dois campos de uma vez. Também invertemos os valores esperados para garantir que a verificação falha quando deve, e falhou. - A terceira verificação de cada passo,
status code check, não está no arquivo Arazzo. O Redocly a acrescenta a partir das respostas listadas no arquivo OpenAPI, então um código de status que a API não documenta reprova o passo mesmo que seus critérios passem.
O workflow limpa quando um passo falha
Os produtos criados na nossa API são compartilhados: quem listar produtos vê o seu até ele ser apagado. Um teste que para no meio deixaria lixo para trás, então readProduct e changePrice têm uma ação onFailure que pula para deleteProduct. Para conferir, mudamos a verificação do preço para que não pudesse passar:
✗ PUT /api/Products/{id} - step changePrice
✓ success criteria check - $statusCode == 200
✗ success criteria check - $response.body#/data/price == $inputs.price
✓ status code check - $statusCode in [200, 404, 500]
Running failure action cleanUp for the step changePrice
✓ DELETE /api/Products/{id} - step deleteProduct
✓ success criteria check - $statusCode == 200
✓ GET /api/Products/{id} - step confirmGone
✓ success criteria check - $statusCode == 404O workflow ainda informa uma falha, mas o produto é apagado antes. Faça o mesmo em qualquer workflow que crie dados em um ambiente compartilhado.
Selector Objects passam no lint mas não rodam
Um Selector Object é a nova forma de tirar um valor de uma resposta. Usamos um para ler a avaliação de um produto com um JSON Pointer e mandá-la para o nosso endpoint echo, que devolve o que recebe:
- stepId: getProduct
operationId: Products_GetById
parameters:
- { name: id, in: path, value: 1 }
outputs:
rating:
context: $response.body
selector: /rating/rate
type: jsonpointer
- stepId: echoIt
operationId: Echo_Echo_POST
requestBody:
contentType: application/json
payload:
rating: $steps.getProduct.outputs.ratingO redocly lint aprova este arquivo. O endpoint echo deveria receber {"rating": 4.3}. Foi isto que ele recebeu:
{
"rating": {
"context": {
"id": 1,
"title": "Wireless Noise-Cancelling Headphones",
"price": 249.99,
...
"rating": { "rate": 4.3, "count": 127 },
"inStock": true,
"createdAt": "2025-06-12T10:30:00Z"
},
"selector": "/rating/rate",
"type": "jsonpointer"
}
}O runner resolveu context como o corpo inteiro da resposta e depois enviou o próprio Selector Object em vez de aplicar o seletor. Nada na saída avisa. O passo só falha porque verificamos o que o servidor recebeu; um workflow que só confere um 200 passaria enviando dados errados.
Até os runners acompanharem, use a forma da 1.0. Para um JSON Pointer ela é até mais curta:
outputs:
rating: $response.body#/rating/rateParâmetros passados para um workflow se perdem
O exemplo que o anúncio usa para parameters é uma ação de falha que chama um workflow de renovação de token e tenta o passo de novo. Nossos endpoints gratuitos não emitem tokens, então testamos o mesmo mecanismo com um produto que não existe: a ação de falha chama um workflow report que envia sua entrada reason para o endpoint echo.
- stepId: getMissing
operationId: Products_GetById
parameters:
- { name: id, in: path, value: 99999 }
successCriteria:
- condition: $statusCode == 200
onFailure:
- name: reportAndRetry
type: retry
retryLimit: 1
workflowId: report
parameters:
- name: reason
value: not foundO runner chama o workflow report e depois tenta o passo de novo, na ordem que a especificação descreve. Mas o endpoint echo recebeu um objeto vazio:
"contentType": "application/json",
"contentLength": 2,
"body": "{}",
"json": {}reason nunca chegou. Rodar report sozinho com --input reason=... funciona, então o problema está em passar parameters pela ação. Numa renovação de token, isso quer dizer que a chamada de renovação sai sem o refresh token.
O que rodou no Redocly CLI 2.62.1
| Recurso do Arazzo 1.1 | Lint | Execução |
|---|---|---|
arazzo: 1.1.0 | Aceito | Roda |
$self | Aceito | Aceito; nada para observar na execução |
| Expression Type Object | Aceito | Roda, e falha quando deve |
dependsOn | Aceito | Aceito; os passos rodam em ordem de qualquer jeito |
| Selector Object | Aceito | Não aplicado: o objeto é enviado como dado |
parameters em ações | Aceito | O workflow roda, as entradas chegam vazias |
| Passos AsyncAPI | Não testado | Não testado |
Não encontramos nenhuma entrada no changelog do Redocly que prometa suporte ao Arazzo 1.1, então talvez sejam apenas recursos que ainda não foram construídos. As outras ferramentas listadas no repositório do Arazzo podem se comportar de outro jeito, e só testamos o Redocly.
Escrevendo workflows 1.1 agora
- Use
arazzo: 1.1.0e os recursos que rodam: Expression Type Objects,$selfedependsOn. - Leia valores da resposta com expressões de runtime como
$response.body#/data/id, não com Selector Objects. - Resolva a renovação de token fazendo login de novo em um passo, não com
parametersnuma ação de falha. - Confira o que o servidor recebeu, não só o código de status. Nem um lint aprovado nem um
200pegaram os problemas deste post. Nosso endpoint echo pegou. - Fixe a versão do runner, como fizemos com
@redocly/cli@2.62.1, para que uma atualização dele não mude seus resultados sem um commit.
Se o seu workflow testa um ciclo de criar, ler, atualizar e apagar, nossa lição de teste CRUD cobre quais verificações cada passo precisa. Lendo uma especificação OpenAPI (em inglês) explica os valores de operationId a que um workflow se refere, e a documentação da API de produtos lista cada campo que o workflow acima usa.