Arazzo 1.1: was neu ist und was heute läuft

Teilen

Am 19. Mai 2026 hat die OpenAPI Initiative Arazzo 1.1.0 veröffentlicht. Arazzo ist das Format der OpenAPI Initiative, um eine Folge von API-Aufrufen zu beschreiben: anmelden, dann eine Bestellung anlegen, dann bezahlen, wobei jeder Schritt Werte an den nächsten weitergibt. OpenAPI sagt, was ein Endpunkt annimmt und zurückgibt. Arazzo sagt, welche Endpunkte in welcher Reihenfolge aufzurufen sind und woran man erkennt, ob ein Aufruf geklappt hat.

Version 1.1 ist das erste Feature-Release seit 1.0 im Jahr 2024. Wir haben einen 1.1-Workflow gegen unsere eigene API geschrieben und mit Redocly CLI 2.62.1 ausgeführt, erschienen am 9. Oktober. Der Runner akzeptiert 1.1-Dokumente, und ein einfacher Workflow aus Anlegen, Lesen, Ändern und Löschen besteht. Zwei der Neuerungen bestehen den Linter, werden beim echten Ausführen aber ignoriert. Dieser Beitrag zeigt, was sich geändert hat, einen Workflow zum Herunterladen und Ausführen und die zwei Neuerungen, die du vorerst meiden solltest.

Was eine Arazzo-Datei tut

Ein Arazzo-Dokument verweist auf eine oder mehrere API-Beschreibungen, meist OpenAPI-Dateien, und listet Workflows auf. Jeder Workflow ist eine Liste von Schritten. Ein Schritt nennt eine Operation über ihre operationId, füllt Parameter und einen Request-Body, legt fest, was als Erfolg zählt, und speichert Werte aus der Antwort für spätere Schritte.

Diese eine Datei ist an drei Stellen nützlich:

  • Tests. Ein Runner führt die Schritte gegen einen echten Server aus und schlägt fehl, wenn ein Erfolgskriterium nicht erfüllt ist. So nutzen wir es unten.
  • Dokumentation. Lesende sehen die Reihenfolge der Aufrufe, die eine Aufgabe braucht, was eine OpenAPI-Datei nicht ausdrücken kann.
  • KI-Agenten. Ein Agent, der OpenAPI liest, weiß, was jeder Endpunkt tut, muss aber raten, wie er sie kombiniert. Ein Arazzo-Workflow sagt es ihm.

Was sich in Arazzo 1.1 geändert hat

Die große Neuerung ist AsyncAPI-Unterstützung, damit ein Workflow HTTP-Aufrufe mit Nachrichtenkanälen mischen kann. Die meisten anderen Änderungen machen HTTP-Workflows leichter zu schreiben. Alle stehen in der Ankündigung:

ÄnderungWas sie tut
Quellen mit type: asyncapiEine Quellbeschreibung kann jetzt ein AsyncAPI-Dokument sein, neben openapi und arazzo.
channelPath, action, correlationId, timeoutSchritte, die Nachrichten senden oder empfangen. action ist send oder receive, correlationId ordnet eine Antwort ihrer Anfrage zu, und timeout ist ein Limit in Millisekunden.
dependsOnEin Schritt kann andere Schritte nennen, die fertig sein müssen, bevor er startet. Einen Schritt zu nennen führt ihn nicht aus.
parameters bei ActionsEine Erfolgs- oder Fehler-Action, die einen anderen Workflow aufruft, kann ihm Eingaben übergeben, etwa um ein Token zu erneuern und dann neu zu versuchen.
Selector ObjectHolt einen Wert aus einer Antwort oder Eingabe, mit JSONPath, XPath oder JSON Pointer. Funktioniert in Outputs, Parameterwerten und Request-Bodies.
Expression Type ObjectLegt die Version einer Abfragesprache fest, etwa JSONPath nach RFC 9535 oder XPath 3.1. Ersetzt das Criterion Expression Type Object aus 1.0.
in: querystringSendet einen Parameter als gesamten Query-String, passend zur gleichen Neuerung in OpenAPI 3.2.
$selfEine URI, die das Dokument identifiziert, damit Verweise darauf nicht davon abhängen, wo die Datei gerade liegt.

Die Spezifikation hat außerdem eine vollständige Grammatik für Runtime-Ausdrücke wie $steps.login.outputs.token bekommen, eine festgelegte Reihenfolge zum Auflösen der Quellbeschreibungen und genaue Regeln dafür, wann eine Bedingung als wahr gilt. Neue Felder kommen damit nicht hinzu, aber sie klären Fälle, in denen zwei Runner unterschiedlicher Meinung sein könnten. Unterstützung für gRPC, GraphQL, MCP und Schleifen steht auf der Roadmap für spätere Versionen und ist nicht in 1.1.

Ein Workflow, den du gegen unsere API ausführen kannst

Dieser Workflow legt auf api.testingapis.com ein Produkt an, liest es zurück, ändert seinen Preis, löscht es und prüft, dass es weg ist. Er nutzt nur kostenlose Endpunkte und läuft daher ohne Konto oder API-Schlüssel.

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

Lade ihn zusammen mit der OpenAPI-Datei herunter, auf die er verweist, leg beide in einen Ordner und führ ihn aus. Node 22.12 oder neuer reicht, denn npx holt den Runner:

Terminal
$ npx @redocly/cli@2.62.1 respect products.arazzo.yaml
Ausgabe (13. Oktober 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

Ein paar Stellen der Datei leisten mehr, als man ihnen ansieht:

  • $response.body#/data/id ist ein Runtime-Ausdruck mit einem JSON Pointer nach dem #. Unser Anlege-Endpunkt verpackt das neue Produkt in { message, data }, die ID steht also unter /data/id. Jeder spätere Schritt liest sie als $steps.createProduct.outputs.productId.
  • Das Kriterium in readProduct nutzt das neue Expression Type Object, um festzulegen, dass die Abfrage JSONPath nach RFC 9535 ist. Eine JSONPath-Bedingung ist erfüllt, wenn der Filter mindestens einen Knoten trifft, $[?@.category == 'electronics' && @.inStock == false] prüft also zwei Felder auf einmal. Wir haben auch die erwarteten Werte umgedreht, um sicherzugehen, dass die Prüfung scheitert, wenn sie soll, und das tat sie.
  • Die dritte Prüfung jedes Schritts, status code check, steht nicht in der Arazzo-Datei. Redocly fügt sie aus den Antworten hinzu, die die OpenAPI-Datei auflistet, ein Statuscode, den die API nicht dokumentiert, lässt den Schritt also scheitern, selbst wenn deine eigenen Kriterien erfüllt sind.

Der Workflow räumt auf, wenn ein Schritt scheitert

Auf unserer API angelegte Produkte sind geteilt: Wer Produkte auflistet, sieht deines, bis es gelöscht ist. Ein Test, der auf halbem Weg stehen bleibt, würde Müll hinterlassen, deshalb haben readProduct und changePrice eine onFailure-Action, die zu deleteProduct springt. Zur Kontrolle haben wir die Preisprüfung so geändert, dass sie nicht bestehen konnte:

Ausgabe mit einem scheiternden Schritt
✗ 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

Der Workflow meldet weiterhin einen Fehler, aber zuerst wird das Produkt gelöscht. Mach es in jedem Workflow so, der in einer geteilten Umgebung Daten anlegt.

Selector Objects bestehen den Lint, laufen aber nicht

Ein Selector Object ist der neue Weg, einen Wert aus einer Antwort zu holen. Wir haben damit die Bewertung eines Produkts per JSON Pointer gelesen und an unseren Echo-Endpunkt geschickt, der zurückgibt, was er empfängt:

selector.arazzo.yaml (Schritte)
- 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 lässt diese Datei durch. Der Echo-Endpunkt sollte {"rating": 4.3} empfangen. Das kam an:

Request-Body, den der Runner gesendet hat
{
  "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"
  }
}

Der Runner hat context zum ganzen Antwort-Body aufgelöst und dann das Selector Object selbst gesendet, statt den Selektor anzuwenden. Nichts in der Ausgabe warnt davor. Der Schritt scheitert nur, weil wir geprüft haben, was der Server empfangen hat; ein Workflow, der nur auf ein 200 prüft, würde bestehen und dabei falsche Daten senden.

Bis die Runner nachziehen, nimm die Form aus 1.0. Für einen JSON Pointer ist sie ohnehin kürzer:

Funktioniert in 1.0 und 1.1
outputs:
  rating: $response.body#/rating/rate

An einen Workflow übergebene Parameter gehen verloren

Das Beispiel der Ankündigung für parameters ist eine Fehler-Action, die einen Workflow zur Token-Erneuerung aufruft und den Schritt wiederholt. Unsere kostenlosen Endpunkte geben keine Tokens aus, also haben wir denselben Mechanismus mit einem fehlenden Produkt getestet: Die Fehler-Action ruft einen Workflow report auf, der seine Eingabe reason an den Echo-Endpunkt schickt.

params.arazzo.yaml (Schritt)
- 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

Der Runner ruft den Workflow report auf und wiederholt dann den Schritt, in der Reihenfolge, die die Spezifikation beschreibt. Aber der Echo-Endpunkt hat ein leeres Objekt bekommen:

Was der Echo-Endpunkt empfangen hat
"contentType": "application/json",
"contentLength": 2,
"body": "{}",
"json": {}

reason kam nie an. report allein mit --input reason=... auszuführen funktioniert, das Problem liegt also im Übergeben von parameters aus der Action. Bei einer Token-Erneuerung heißt das: Der Aufruf geht ohne Refresh-Token raus.

Was in Redocly CLI 2.62.1 lief

Neuerung in Arazzo 1.1LintAusführung
arazzo: 1.1.0AkzeptiertLäuft
$selfAkzeptiertAkzeptiert; zur Laufzeit gibt es nichts zu beobachten
Expression Type ObjectAkzeptiertLäuft, und scheitert, wenn es soll
dependsOnAkzeptiertAkzeptiert; die Schritte laufen ohnehin der Reihe nach
Selector ObjectAkzeptiertNicht angewandt: das Objekt wird als Daten gesendet
parameters bei ActionsAkzeptiertWorkflow läuft, Eingaben sind leer
AsyncAPI-SchritteNicht getestetNicht getestet

Wir haben keinen Eintrag im Changelog von Redocly gefunden, der Arazzo-1.1-Unterstützung verspricht, es sind also womöglich einfach Funktionen, die noch nicht gebaut sind. Die anderen Tools im Arazzo-Repository verhalten sich vielleicht anders, und wir haben nur Redocly getestet.

1.1-Workflows heute schreiben

  • Nutze arazzo: 1.1.0 und die Neuerungen, die laufen: Expression Type Objects, $self und dependsOn.
  • Lies Werte aus der Antwort mit Runtime-Ausdrücken wie $response.body#/data/id, nicht mit Selector Objects.
  • Löse die Token-Erneuerung, indem du dich in einem Schritt neu anmeldest, nicht über parameters an einer Fehler-Action.
  • Prüfe, was der Server empfangen hat, nicht nur den Statuscode. Weder ein bestandener Lint noch ein 200 haben eines der Probleme in diesem Beitrag gefunden. Unser Echo-Endpunkt schon.
  • Fixiere die Runner-Version, wie wir es mit @redocly/cli@2.62.1 getan haben, damit ein Update deine Ergebnisse nicht ohne Commit ändern kann.

Testet dein Workflow einen Zyklus aus Anlegen, Lesen, Ändern und Löschen, behandelt unsere Lektion zum CRUD-Testing, welche Prüfungen jeder Schritt braucht. Eine OpenAPI-Spezifikation lesen (auf Englisch) erklärt die Werte von operationId, auf die ein Workflow verweist, und die Doku zur Produkt-API listet jedes Feld, das der Workflow oben berührt.