Am schnellsten versteht man Arazzo, wenn man selbst eine Spec erstellt. Dieses Tutorial beschreibt deshalb Schritt für Schritt einen vollständigen Workflow, vom Login über das Anlegen einer Bestellung bis zur Statusprüfung. Das Grundgerüst dafür passt in zwanzig Zeilen.
info:
title: Bestellung mit Statusverfolgung
version: 1.0.0
sourceDescriptions:
- name: shopApi
url: ./openapi.yaml
type: openapi
workflows:
- workflowId: placeAndTrackOrder
steps:
- stepId: login
operationId: createSession
- stepId: createOrder
operationId: createOrder
- stepId: checkStatus
operationId: getOrderStatus
Drei Schritte über eine API, noch ohne Datenflüsse und Fehlerbehandlung. Genau dieses Gerüst erweitert der Rest des Tutorials zu einer vollständigen, prüfbaren Beschreibung. Wer zuerst wissen will, was Arazzo grundsätzlich ist und wofür es gedacht ist, findet die Einordnung im Artikel Was ist Arazzo. Hier geht es um die Umsetzung.
Eine Arazzo-Spec beschreibt einen mehrstufigen API-Workflow als YAML- oder JSON-Datei. Sie referenziert über sourceDescriptions eine oder mehrere OpenAPI-Beschreibungen, definiert unter workflows die Schritte eines Ablaufs und legt mit outputs, parameters und successCriteria fest, wie Daten von Schritt zu Schritt fließen und wann ein Schritt als gelungen gilt. Die fertige Datei lässt sich validieren, versionieren und von Werkzeugen wie Testrunnern oder KI-Agenten ausführen.
Der Use Case und die Ausgangslage
Der Beispiel-Workflow bildet einen Vorgang ab, den fast jede Commerce-API kennt. Ein Konsument meldet sich an, legt eine Bestellung an und prüft anschließend deren Status. Drei Endpoints, drei Abhängigkeiten. Der Login liefert ein Token, das die Bestellung braucht. Die Bestellung liefert eine ID, die die Statusabfrage braucht. Genau diese Übergaben sind der Teil, den eine OpenAPI-Spec allein nicht ausdrückt und den Arazzo beschreibt.
Als Ausgangslage dient eine vorhandene OpenAPI-Datei openapi.yaml mit den drei Operationen. Wichtig für das Tutorial sind nur ihre operationId-Werte, denn über sie verbindet Arazzo seine Schritte mit den Operationen.
paths:
/sessions:
post:
operationId: createSession
/orders:
post:
operationId: createOrder
/orders/{orderId}/status:
get:
operationId: getOrderStatus
Eine saubere OpenAPI-Spec mit sprechenden operationId-Werten ist damit die halbe Arazzo-Arbeit. Wer dort kryptische oder generierte IDs stehen hat, sollte sie vor dem ersten Workflow aufräumen, weil jede Arazzo-Datei sie wörtlich referenziert.
Die Quellen referenzieren
Am Anfang jeder Arazzo-Datei stehen drei Angaben. Die Versionszeile legt fest, gegen welche Arazzo-Version die Datei geschrieben ist. Der info-Block benennt den Workflow für Menschen. Und sourceDescriptions verbindet die Datei mit den zugrunde liegenden API-Beschreibungen.
info:
title: Bestellung mit Statusverfolgung
version: 1.0.0
description: Login, Bestellung anlegen, Status verfolgen
sourceDescriptions:
- name: shopApi
url: ./openapi.yaml
type: openapi
Der name der Quelle ist mehr als Dekoration. Sind mehrere Quellen im Spiel, etwa eine zweite API für die Zahlung, adressieren die Schritte ihre Operationen über diesen Namen. Für den Einstieg reicht eine Quelle. Im Tutorial zeigt der Verweis relativ auf eine lokale Datei, in der Praxis verweist er auf die veröffentlichte Spec der jeweiligen API.
Der Login-Schritt und seine Outputs
Der erste Schritt authentifiziert den Konsumenten. Interessant ist an ihm vor allem das Ende, denn dort wird das Token für die folgenden Schritte herausgezogen.
- stepId: login
operationId: createSession
requestBody:
payload:
apiKey: $inputs.apiKey
successCriteria:
- condition: $statusCode == 201
outputs:
token: $response.body#/accessToken
Hier arbeiten drei Mechanismen zusammen. Der Ausdruck $inputs.apiKey greift auf die Eingaben des Workflows zu, die später noch definiert werden. Das successCriteria legt fest, dass nur ein Status 201 als Erfolg zählt, alles andere bricht den Schritt ab. Und die outputs extrahieren das Token aus der Antwort und stellen es unter dem Namen token bereit. Ab jetzt kann jeder spätere Schritt darauf zugreifen.
Die Syntax hinter $response.body#/accessToken ist ein JSON Pointer hinter einem Laufzeit-Ausdruck. Der Teil vor der Raute benennt die Quelle, der Teil danach den Pfad ins Dokument. Wer tiefer verschachtelte Strukturen oder XML-Antworten auswerten muss, nutzt seit Arazzo 1.1.0 zusätzlich das Selector Object mit jsonpath oder xpath, eine der Neuerungen, die Was ist neu in Arazzo 1.1 vorstellt.
Die Bestellung und der Datenfluss
Der zweite Schritt verwendet das Token und erzeugt seinerseits die Bestell-ID. Damit zeigt er das Muster, das jeden Arazzo-Workflow trägt, nämlich die Kette aus Output und Zugriff.
operationId: createOrder
parameters:
- name: Authorization
in: header
value: Bearer $steps.login.outputs.token
requestBody:
payload:
items: $inputs.items
successCriteria:
- condition: $statusCode == 201
outputs:
orderId: $response.body#/id
Der Ausdruck $steps.login.outputs.token liest sich von links nach rechts als Pfad durch den Workflow. Aus dem Schritt login das Output-Feld token, eingesetzt als Bearer-Header. Diese Referenzen sind explizit und prüfbar, und genau das unterscheidet die Spec von einer Prosa-Beschreibung, in der die Übergabe nur behauptet wird. Ein Validator erkennt einen Tippfehler im Schrittnamen sofort, ein Wiki-Text verzeiht ihn jahrelang.
Erwähnenswert ist die Doppelrolle der parameters. Sie setzen Header, Query- oder Pfad-Parameter und können Werte aus Inputs, vorherigen Schritten oder Konstanten beziehen. Damit decken sie praktisch jede Stelle ab, an der ein API-Aufruf von außen parametrisiert wird.
Die Statusprüfung mit Wiederholung
Der dritte Schritt fragt den Status der Bestellung ab, und hier kommt eine Anforderung dazu, die in echten Systemen häufig auftritt. Der Status steht selten sofort auf dem Zielwert, weil die Verarbeitung im Hintergrund läuft. Der Schritt darf also nicht beim ersten Versuch aufgeben.
operationId: getOrderStatus
parameters:
- name: orderId
in: path
value: $steps.createOrder.outputs.orderId
- name: Authorization
in: header
value: Bearer $steps.login.outputs.token
successCriteria:
- condition: $response.body#/status == "confirmed"
onFailure:
- name: retryStatus
type: retry
retryAfter: 2
retryLimit: 10
outputs:
finalStatus: $response.body#/status
Das onFailure mit dem Typ retry wiederholt den Schritt bis zu zehnmal im Abstand von zwei Sekunden, bis das Erfolgskriterium erfüllt ist. Damit ist das verbreitete Polling-Muster inklusive seiner Grenzen deklarativ beschrieben. Nach dem zehnten Versuch gilt der Schritt endgültig als gescheitert, und der Workflow endet kontrolliert, ohne endlos zu warten.
Neben retry kennt onFailure auch die Typen goto für den Sprung zu einem anderen Schritt und end für den sofortigen Abbruch. Wer Kompensations-Schritte beschreiben will, etwa das Stornieren der Bestellung nach einem endgültig gescheiterten Folgeschritt, kombiniert goto mit einem eigens definierten Aufräum-Schritt. Die konzeptionelle Seite dieser Fehlerpfade, von Idempotenz bis zum Saga-Muster, behandelt der Überblick zur API-Orchestrierung.
Eine zweite API kommt dazu
Reale Abläufe bleiben selten innerhalb einer API, und genau für diesen Fall ist sourceDescriptions eine Liste. Angenommen, die Zahlung läuft über eine eigene Payment-API mit eigener OpenAPI-Datei. Dann wächst der Kopf der Spec um eine Quelle, und die Schritte adressieren ihre Operationen mit dem Quellnamen als Präfix.
- name: shopApi
url: ./openapi.yaml
type: openapi
- name: paymentApi
url: ./payment-openapi.yaml
type: openapi
# Im Workflow: Operation eindeutig über die Quelle adressieren
- stepId: authorizePayment
operationId: paymentApi.authorizePayment
parameters:
- name: Authorization
in: header
value: Bearer $steps.login.outputs.token
successCriteria:
- condition: $statusCode == 200
outputs:
paymentId: $response.body#/id
Das Präfix ist nur nötig, wenn eine operationId in mehreren Quellen vorkommt oder Verwechslungsgefahr besteht, aber es schadet auch sonst nicht und macht die Herkunft jeder Operation auf einen Blick lesbar. Spätestens an dieser Stelle zeigt sich der eigentliche Wert der Spec. Ein Ablauf über zwei APIs hat im Normalfall keinen gemeinsamen Ort mehr, an dem er beschrieben wäre, weil jede API nur ihre eigene Dokumentation pflegt. Die Arazzo-Datei ist genau dieser Ort.
Mit der zweiten Quelle stellt sich auch die Frage nach dem Eigentümer der Workflow-Datei neu, denn der Ablauf gehört nun keinem der beiden API-Teams allein. Bewährt hat sich die Verortung beim Team des fachlichen Prozesses, das die Datei pflegt und beide API-Teams bei Änderungen einbindet. Spätestens hier zeigt sich, warum die Workflow-Spec ein eigenes Repository braucht. Sie ist ein eigenständiges Asset mit eigener Versionierung und eigenen Review-Prozessen und gehört in keines der beteiligten API-Repositories.
Inputs und Outputs des Workflows
Bisher haben die Schritte auf $inputs zugegriffen, ohne dass diese definiert wären. Der Workflow deklariert seine Eingaben als JSON-Schema und seine Ausgaben als benannte Ausdrücke, womit er nach außen selbst wie eine Funktion aussieht.
inputs:
type: object
required: [apiKey, items]
properties:
apiKey:
type: string
items:
type: array
items:
type: object
outputs:
orderId: $steps.createOrder.outputs.orderId
finalStatus: $steps.checkStatus.outputs.finalStatus
Diese Schnittstelle zahlt sich doppelt aus. Ein Testrunner weiß damit, welche Daten er bereitstellen muss und welche Ergebnisse er prüfen kann. Und seit Arazzo 1.1.0 kann ein anderer Workflow diesen hier als Baustein aufrufen und ihm über genau diese Inputs Werte übergeben, womit sich größere Abläufe aus kleineren zusammensetzen lassen.
Validieren, ausführen, einchecken
Eine Arazzo-Datei entfaltet ihren Wert erst, wenn sie geprüft und ausgeführt wird. Dafür haben sich drei Stationen bewährt.
- Validierung gegen die Spezifikation. Ein Arazzo-Validator prüft Struktur, Pflichtfelder und die Auflösbarkeit aller Referenzen, also auch, ob jede
operationIdin der referenzierten OpenAPI-Datei existiert. Diese Prüfung gehört als Job in die CI-Pipeline neben das OpenAPI-Linting. - Ausführung gegen eine Testumgebung. Ein Testrunner arbeitet die Schritte gegen eine laufende API ab, füllt die Inputs mit Testdaten und meldet, an welchem Schritt und welchem Kriterium ein Lauf scheitert. Damit wird aus der Dokumentation ein End-to-End-Test.
- Pflege unter Versionskontrolle. Die Arazzo-Datei gehört in ein eigenes Repository, mit eigenen Reviews, Tags und Releases und der Version im
info-Block. Sie ist ein eigenständiges Asset mit eigenem Lebenszyklus, und diese Versionierung funktioniert nur sauber, wenn sie sich nicht mit den Release-Ständen einer einzelnen API vermischt.
In einem Team, das diesen Dreiklang eingeführt hat, scheiterte der nächtliche Workflow-Lauf einige Wochen später überraschend am Status-Schritt. Die Backend-Kollegen hatten den Statuswert von „confirmed" auf „completed" umbenannt und die Änderung für rein kosmetisch gehalten. Der Lauf machte den Bruch am nächsten Morgen sichtbar, lange bevor ein Konsument ihn bemerkte. Die Wiki-Dokumentation desselben Ablaufs hätte den alten Wert vermutlich noch Monate behauptet.
Für die Pipeline-Integration genügt im einfachsten Fall ein zusätzlicher Job, der beide Prüfungen nacheinander ausführt.
arazzo-check:
stage: test
script:
- arazzo validate workflows/place-and-track-order.arazzo.yaml
- arazzo run workflows/place-and-track-order.arazzo.yaml
--server https://staging.example.com
--input apiKey=$STAGING_API_KEY
--input items=@testdata/items.json
Die Werkzeuglandschaft rund um Arazzo ist jung, aber für Validierung und Ausführung gibt es bereits mehrere Optionen, von Open-Source-Runnern bis zu Editoren mit eingebauter Prüfung. Welche Kombination passt, hängt vor allem davon ab, ob der Lauf in der Pipeline oder am Entwickler-Arbeitsplatz stattfinden soll. Wichtiger als die Werkzeugwahl ist die Regel dahinter. Eine Workflow-Datei, die nirgends ausgeführt wird, altert genauso schnell wie das Wiki, das sie ersetzen sollte.
Typische Fallstricke beim ersten Workflow
Ein paar typische Schwierigkeiten beim Einstieg verdienen einen Hinweis vorab.
- Verwechselte Ausdrucks-Kontexte.
$response.bodyfunktioniert nur inoutputsundsuccessCriteriades eigenen Schritts, während spätere Schritte über$steps.name.outputs.feldzugreifen. Der direkte Zugriff auf die Antwort eines früheren Schritts ist der häufigste Anfängerfehler, und der Validator meldet ihn nicht immer eindeutig. - Vergessene Anführungszeichen bei Vergleichswerten. Die Bedingung
$response.body#/status == confirmedschlägt still fehl, weil der Vergleichswert als Ausdruck und nicht als Zeichenkette gelesen wird. Mit== "confirmed"ist die Absicht eindeutig. - Zu strenge Erfolgskriterien. Wer auf exakte Antwortzeiten oder vollständige Body-Strukturen prüft, baut sich einen Test, der bei jeder harmlosen API-Erweiterung bricht. Bewährt haben sich Kriterien auf Statuscode plus die fachlich entscheidenden Felder, nicht mehr.
- Workflows als Kopie der OpenAPI-Beschreibung. Eine Arazzo-Datei, die nur jeden Endpoint einmal aufruft, ohne dass Daten fließen, dokumentiert nichts, was die OpenAPI-Spec nicht schon sagt. Der Mehrwert entsteht erst durch Abhängigkeiten und Fehlerpfade.
- Geheimnisse in der Spec. API-Keys und Tokens gehören in die Inputs und werden erst zur Laufzeit übergeben, wie im CI-Beispiel über Variablen. Andernfalls landet eine Workflow-Datei mit eingebettetem Schlüssel samt Geheimnis im Repository.
Keiner dieser Punkte ist ein Grund, den Einstieg zu verschieben. Sie kosten beim ersten Workflow je eine halbe Stunde Sucherei und sind danach Routine.
Den Workflow lesbar halten
Eine Arazzo-Datei wird von mehr Menschen gelesen als geschrieben, von Konsumenten, Reviewern und in wachsendem Maß von KI-Agenten, die sie als Bauplan nutzen. Ein paar Konventionen halten sie für alle drei Gruppen verständlich.
Die stepId-Werte verdienen dieselbe Sorgfalt wie Funktionsnamen im Code. Ein Schritt namens checkStatus erklärt sich selbst, einer namens step3 erklärt gar nichts, und weil spätere Schritte über diese Namen auf Outputs zugreifen, wandern unklare Namen durch die ganze Datei. Dasselbe gilt für die Output-Namen, die als Schnittstelle zwischen den Schritten fungieren.
Jeder Schritt akzeptiert zusätzlich eine description, und es lohnt sich, sie für das Warum zu nutzen statt für das Was. Dass der Schritt den Status abfragt, sieht jeder an der Operation. Warum er bis zu zehnmal wiederholt wird und weshalb erst „confirmed" als Erfolg zählt, steht sonst nirgendwo. Gerade für Agenten, die den Workflow ausführen, ist diese Begründungsebene wertvoll, weil sie Kontext liefert, den die reine Struktur nicht trägt. Wie APIs insgesamt für maschinelle Konsumenten lesbar werden, behandelt der Artikel AI-Ready APIs.
Bei wachsender Zahl von Workflows hat sich außerdem eine Datei pro Workflow bewährt, benannt nach der workflowId, statt einer Sammeldatei mit zehn Abläufen. Die Einzeldateien lassen sich gezielt versionieren, reviewen und im Katalog einzeln auffindbar machen, und die Verkettung über Workflow-Aufrufe funktioniert über Dateigrenzen hinweg, weil sourceDescriptions auch Arazzo-Dokumente referenzieren kann.
Wohin mit der fertigen Spec
Mit der lauffähigen Datei stellt sich die Frage nach der Sichtbarkeit, und sie entscheidet darüber, ob die Arbeit sich über das eigene Team hinaus auszahlt. In seinem Repository ist der Workflow gut aufgehoben für alle, die an den Abläufen arbeiten. Konsumenten der API schauen dort allerdings selten hinein. Sie suchen im Developer Portal, und dort finden sie üblicherweise die Endpoints, aber nicht die Abläufe.
Der nächste konsequente Schritt ist deshalb, die Workflow-Beschreibung dort zu veröffentlichen, wo auch die API-Dokumentation lebt, also im Katalog des Portals. Dort lässt sich eine Arazzo-Datei genauso verwalten wie eine OpenAPI-Spec, mit Zuständigkeiten, Versionen und einem Audit-Trail, der auch regulierten Anforderungen standhält. Ein Konsument, der die Bestell-API entdeckt, sieht dann direkt daneben den beschriebenen Ablauf und muss die Reihenfolge der Aufrufe nicht mehr aus der Endpoint-Liste erraten. Was das organisatorisch bedeutet und wie Workflows als eigenständige Katalog-Inhalte funktionieren, behandelt der Beitrag Arazzo-Workflows im API-Katalog.
Die fertige Datei im Überblick
Zum Abschluss die vollständige Spec, wie sie nach allen Schritten im Repository liegt. Sie ist bewusst kompakt gehalten und kommt ohne Spezialfälle aus, taugt aber als Vorlage für den ersten eigenen Workflow.
info:
title: Bestellung mit Statusverfolgung
version: 1.0.0
sourceDescriptions:
- name: shopApi
url: ./openapi.yaml
type: openapi
workflows:
- workflowId: placeAndTrackOrder
inputs:
type: object
required: [apiKey, items]
properties:
apiKey: { type: string }
items: { type: array }
steps:
- stepId: login
operationId: createSession
requestBody:
payload:
apiKey: $inputs.apiKey
successCriteria:
- condition: $statusCode == 201
outputs:
token: $response.body#/accessToken
- stepId: createOrder
operationId: createOrder
parameters:
- name: Authorization
in: header
value: Bearer $steps.login.outputs.token
requestBody:
payload:
items: $inputs.items
successCriteria:
- condition: $statusCode == 201
outputs:
orderId: $response.body#/id
- stepId: checkStatus
operationId: getOrderStatus
parameters:
- name: orderId
in: path
value: $steps.createOrder.outputs.orderId
- name: Authorization
in: header
value: Bearer $steps.login.outputs.token
successCriteria:
- condition: $response.body#/status == "confirmed"
onFailure:
- name: retryStatus
type: retry
retryAfter: 2
retryLimit: 10
outputs:
finalStatus: $response.body#/status
outputs:
orderId: $steps.createOrder.outputs.orderId
finalStatus: $steps.checkStatus.outputs.finalStatus
Der beste Kandidat für die erste eigene Arazzo-Spec ist ein Ablauf, der bereits als Prosa-Anleitung im Wiki existiert und von Konsumenten nachgefragt wird. Die Übersetzung in eine Spec dauert meist unter einem Tag, und die Abweichungen zwischen Anleitung und tatsächlichem API-Verhalten, die dabei auffallen, sind den Aufwand oft allein wert.
So entwickeln Sie den Workflow weiter
Mit der ersten lauffähigen Spec sind die Anschlussfragen meist schnell auf dem Tisch. Mehrere Quellen für Abläufe über API-Grenzen hinweg funktionieren über zusätzliche Einträge in sourceDescriptions. Event-Schritte, die auf eine Nachricht warten, kommen mit den AsyncAPI-Erweiterungen aus Arazzo 1.1.0 dazu. Und sobald mehrere Workflows entstehen, lohnt es sich, wiederkehrende Teilabläufe als eigene Workflows auszulagern und aufzurufen.
Beschreiben Sie als nächsten Schritt einen zweiten Workflow aus Ihrem Bestand, diesmal einen mit einem echten Kompensations-Fall, etwa einer Stornierung. An genau dieser Stelle zeigt sich, ob die Fehlerpfade Ihrer APIs so klar definiert sind, wie die Dokumentation behauptet.