Arazzo 1.1 : les nouveautés, et ce qui tourne aujourd’hui

Partager

Le 19 mai 2026, l’OpenAPI Initiative a publié Arazzo 1.1.0. Arazzo est le format de l’OpenAPI Initiative pour décrire une suite d’appels d’API : se connecter, puis créer une commande, puis la payer, chaque étape passant des valeurs à la suivante. OpenAPI dit ce qu’un point d’accès accepte et renvoie. Arazzo dit quels points d’accès appeler, dans quel ordre, et comment savoir si chaque appel a fonctionné.

La version 1.1 est la première à apporter des nouveautés depuis la 1.0 de 2024. Nous avons écrit un workflow 1.1 contre notre propre API et l’avons lancé avec Redocly CLI 2.62.1, sorti le 9 octobre. Le runner accepte les documents 1.1, et un workflow simple de création, lecture, modification et suppression passe. Deux des nouveautés passent le linter mais sont ignorées quand le workflow s’exécute vraiment. Cet article présente ce qui a changé, un workflow à télécharger et à lancer, et les deux nouveautés à éviter pour l’instant.

Ce que fait un fichier Arazzo

Un document Arazzo pointe vers une ou plusieurs descriptions d’API, généralement des fichiers OpenAPI, et liste des workflows. Chaque workflow est une liste d’étapes. Une étape désigne une opération par son operationId, remplit les paramètres et un corps de requête, dit ce qui compte comme un succès et garde des valeurs de la réponse pour les étapes suivantes.

Ce seul fichier sert à trois endroits :

  • Tests. Un runner exécute les étapes contre un vrai serveur et échoue si un critère de succès n’est pas rempli. C’est ainsi que nous l’utilisons plus bas.
  • Documentation. Le lecteur voit l’ordre des appels qu’exige une tâche, ce qu’un fichier OpenAPI ne peut pas exprimer.
  • Agents d’IA. Un agent qui lit OpenAPI sait ce que fait chaque point d’accès, mais doit deviner comment les combiner. Un workflow Arazzo le lui dit.

Ce qui a changé dans Arazzo 1.1

La grande nouveauté est la prise en charge d’AsyncAPI, pour qu’un workflow mélange appels HTTP et canaux de messages. La plupart des autres changements facilitent l’écriture des workflows HTTP. Tout est listé dans l’annonce de la version :

ChangementCe qu’il fait
Sources type: asyncapiUne description source peut désormais être un document AsyncAPI, à côté d’openapi et d’arazzo.
channelPath, action, correlationId, timeoutDes étapes qui envoient ou reçoivent des messages. action vaut send ou receive, correlationId associe une réponse à sa requête, et timeout est une limite en millisecondes.
dependsOnUne étape peut lister d’autres étapes qui doivent se terminer avant qu’elle commence. Lister une étape ne l’exécute pas.
parameters sur les actionsUne action de succès ou d’échec qui appelle un autre workflow peut lui passer des entrées, par exemple pour renouveler un token puis réessayer.
Selector ObjectExtrait une valeur d’une réponse ou d’une entrée avec JSONPath, XPath ou JSON Pointer. Fonctionne dans les outputs, les valeurs de paramètres et les corps de requête.
Expression Type ObjectFixe la version d’un langage de requête, comme JSONPath selon la RFC 9535 ou XPath 3.1. Remplace le Criterion Expression Type Object de la 1.0.
in: querystringEnvoie un paramètre comme query string entière, à l’image du même ajout dans OpenAPI 3.2.
$selfUne URI qui identifie le document, pour que les références vers lui ne dépendent pas de l’endroit où le fichier est stocké.

La spécification a aussi gagné une grammaire complète pour les expressions d’exécution comme $steps.login.outputs.token, un ordre défini pour résoudre les descriptions sources et des règles exactes sur le moment où une condition est vraie. Elles n’ajoutent pas de champs, mais tranchent des cas où deux runners auraient pu diverger. La prise en charge de gRPC, GraphQL, MCP et des boucles figure sur la feuille de route des versions suivantes et n’est pas dans la 1.1.

Un workflow à lancer contre notre API

Ce workflow crée un produit sur api.testingapis.com, le relit, change son prix, le supprime et vérifie qu’il a disparu. Il n’utilise que des points d’accès gratuits, et tourne donc sans compte ni clé d’API.

products.arazzo.yaml
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.productId

Téléchargez-le avec le fichier OpenAPI vers lequel il pointe, mettez les deux dans un dossier et lancez-le. Node 22.12 ou plus récent suffit, puisque npx récupère le runner :

Terminal
$ npx @redocly/cli@2.62.1 respect products.arazzo.yaml
Sortie (13 octobre 2026)
Running 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: 922ms

Quelques passages du fichier font plus qu’il n’y paraît :

  • $response.body#/data/id est une expression d’exécution avec un JSON Pointer après le #. Notre point d’accès de création enveloppe le nouveau produit dans { message, data }, l’ID se trouve donc en /data/id. Chaque étape suivante le lit comme $steps.createProduct.outputs.productId.
  • Le critère de readProduct utilise le nouvel Expression Type Object pour dire que la requête est du JSONPath selon la RFC 9535. Une condition JSONPath passe quand le filtre trouve au moins un nœud, donc $[?@.category == 'electronics' && @.inStock == false] vérifie deux champs à la fois. Nous avons aussi inversé les valeurs attendues pour nous assurer que la vérification échoue quand il le faut, et elle a échoué.
  • La troisième vérification de chaque étape, status code check, n’est pas dans le fichier Arazzo. Redocly l’ajoute à partir des réponses que liste le fichier OpenAPI, si bien qu’un code de statut non documenté par l’API fait échouer l’étape même si vos propres critères passent.

Le workflow fait le ménage quand une étape échoue

Les produits créés sur notre API sont partagés : quiconque liste les produits voit le vôtre jusqu’à sa suppression. Un test qui s’arrête à mi-chemin laisserait des déchets, donc readProduct et changePrice ont une action onFailure qui saute vers deleteProduct. Pour le vérifier, nous avons modifié le contrôle du prix pour qu’il ne puisse pas passer :

Sortie avec une étape en échec
✗ 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 == 404

Le workflow signale toujours un échec, mais le produit est supprimé avant. Faites de même dans tout workflow qui crée des données dans un environnement partagé.

Les Selector Objects passent le lint mais ne s’exécutent pas

Un Selector Object est la nouvelle façon d’extraire une valeur d’une réponse. Nous en avons utilisé un pour lire la note d’un produit avec un JSON Pointer et l’envoyer à notre point d’accès echo, qui renvoie ce qu’il reçoit :

selector.arazzo.yaml (étapes)
- 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.rating

redocly lint valide ce fichier. Le point d’accès echo devrait recevoir {"rating": 4.3}. Voici ce qu’il a reçu :

Corps de requête envoyé par le runner
{
  "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"
  }
}

Le runner a résolu context en corps de réponse complet, puis a envoyé le Selector Object lui-même au lieu d’appliquer le sélecteur. Rien dans la sortie ne le signale. L’étape n’échoue que parce que nous avons vérifié ce que le serveur a reçu ; un workflow qui se contente de vérifier un 200 passerait en envoyant de mauvaises données.

En attendant que les runners suivent, utilisez la forme de la 1.0. Pour un JSON Pointer, elle est d’ailleurs plus courte :

Fonctionne en 1.0 et en 1.1
outputs:
  rating: $response.body#/rating/rate

Les paramètres passés à un workflow se perdent

L’exemple de l’annonce pour parameters est une action d’échec qui appelle un workflow de renouvellement de token puis relance l’étape. Nos points d’accès gratuits ne délivrent pas de tokens, alors nous avons testé le même mécanisme avec un produit inexistant : l’action d’échec appelle un workflow report qui envoie son entrée reason au point d’accès echo.

params.arazzo.yaml (étape)
- 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 found

Le runner appelle bien le workflow report puis relance l’étape, dans l’ordre que décrit la spécification. Mais le point d’accès echo a reçu un objet vide :

Ce que le point d’accès echo a reçu
"contentType": "application/json",
"contentLength": 2,
"body": "{}",
"json": {}

reason n’est jamais arrivé. Lancer report seul avec --input reason=... fonctionne, le problème vient donc du passage de parameters depuis l’action. Pour un renouvellement de token, cela signifie que l’appel part sans refresh token.

Ce qui a tourné dans Redocly CLI 2.62.1

Nouveauté d’Arazzo 1.1LintExécution
arazzo: 1.1.0AcceptéS’exécute
$selfAcceptéAccepté ; rien à observer à l’exécution
Expression Type ObjectAcceptéS’exécute, et échoue quand il le faut
dependsOnAcceptéAccepté ; les étapes s’exécutent de toute façon dans l’ordre
Selector ObjectAcceptéNon appliqué : l’objet est envoyé comme donnée
parameters sur les actionsAcceptéLe workflow s’exécute, les entrées sont vides
Étapes AsyncAPINon testéNon testé

Nous n’avons trouvé aucune entrée du changelog de Redocly qui annonce la prise en charge d’Arazzo 1.1 ; il s’agit donc peut-être simplement de fonctionnalités pas encore développées. Les autres outils listés dans le dépôt Arazzo peuvent se comporter autrement, et nous n’avons testé que Redocly.

Écrire des workflows 1.1 dès maintenant

  • Utilisez arazzo: 1.1.0 et les nouveautés qui s’exécutent : Expression Type Objects, $self et dependsOn.
  • Lisez les valeurs de réponse avec des expressions d’exécution comme $response.body#/data/id, pas avec des Selector Objects.
  • Gérez le renouvellement de token en vous reconnectant dans une étape, pas via parameters sur une action d’échec.
  • Vérifiez ce que le serveur a reçu, pas seulement le code de statut. Ni un lint réussi ni un 200 n’ont repéré les problèmes de cet article. Notre point d’accès echo, si.
  • Fixez la version du runner, comme nous l’avons fait avec @redocly/cli@2.62.1, pour qu’une mise à jour ne change pas vos résultats sans commit.

Si votre workflow teste un cycle de création, lecture, modification et suppression, notre leçon sur le test CRUD détaille les vérifications de chaque étape. Lire une spécification OpenAPI (en anglais) explique les valeurs operationId auxquelles se réfère un workflow, et la documentation de l’API de produits liste chaque champ que touche le workflow ci-dessus.