<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://usearazzo.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://usearazzo.com/" rel="alternate" type="text/html" /><updated>2026-08-29T21:49:38+00:00</updated><id>https://usearazzo.com/feed.xml</id><title type="html">UseArazzo</title><subtitle>UseArazzo is a JavaScript/TypeScript toolkit for Arazzo, the OpenAPI Initiative&apos;s specification for multi-step API workflows. Validate documents, run workflows against live APIs, and read the trace of each step.</subtitle><entry><title type="html">I Diffed Every Arazzo Release So You Don’t Have To</title><link href="https://usearazzo.com/blog/arazzo-specification-evolution/" rel="alternate" type="text/html" title="I Diffed Every Arazzo Release So You Don’t Have To" /><published>2026-08-29T00:00:00+00:00</published><updated>2026-08-29T00:00:00+00:00</updated><id>https://usearazzo.com/blog/arazzo-specification-evolution</id><content type="html" xml:base="https://usearazzo.com/blog/arazzo-specification-evolution/"><![CDATA[<p>When you write an Arazzo document, “which version” is a line at the top you rarely think about. When you implement Arazzo, it’s most of the job. A validator has to know exactly what 1.0.0 accepted that 1.0.1 doesn’t, and a runner has to know what a success criterion means under 1.1.0 when 1.0.1 never quite said.</p>

<p>So while building the <a href="https://github.com/usearazzo/arazzo-toolkit">UseArazzo toolkit</a>, I couldn’t rely on a rough sense of how the specification has evolved. I needed the exact diff. I downloaded the three spec texts, diffed them line by line, cross-checked every hunk against the release notes and the pull requests behind it, and kept notes as I went, because the <a href="/validator/">Validator</a> has to draw the line per version and the <a href="/runner/">Runner</a> has to honour it.</p>

<p>Those notes, tidied up, are this article. It’s for anyone who has to care what the <code class="language-plaintext highlighter-rouge">arazzo:</code> line at the top of a document means: people writing the documents, and people writing the tools that read them. If yours still say <code class="language-plaintext highlighter-rouge">arazzo: 1.0.0</code>, here is what changed since, what you need to do about it, and what you’re now allowed to say that you couldn’t before.</p>

<h2 id="at-a-glance">Three releases at a glance</h2>

<table>
  <thead>
    <tr>
      <th>Version</th>
      <th>Released</th>
      <th>Nature</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://spec.openapis.org/arazzo/v1.0.0.html">1.0.0</a></td>
      <td>September 2024</td>
      <td>Initial stable release</td>
    </tr>
    <tr>
      <td><a href="https://spec.openapis.org/arazzo/v1.0.1.html">1.0.1</a></td>
      <td>January 2025</td>
      <td>Patch: erratum fixes and clarifications, plus the first official JSON Schema</td>
    </tr>
    <tr>
      <td><a href="https://spec.openapis.org/arazzo/v1.1.0.html">1.1.0</a></td>
      <td>May 2026</td>
      <td>Minor: AsyncAPI v3 sources, Selector Objects, step dependencies, document identity, tightened evaluation semantics</td>
    </tr>
  </tbody>
</table>

<p>The short version: 1.0.1 removed three things that never worked and fixed the examples. 1.1.0 added a lot and, on paper, broke nothing. The rest of the post is the long version.</p>

<h2 id="from-1-0-0-to-1-0-1">From 1.0.0 to 1.0.1: the patch</h2>

<p>1.0.1 adds no objects, no fields, and no enum values. Every change is an erratum fix or a clarification. A patch release isn’t allowed to break anything, and this one doesn’t in the versioning sense: what it removes never worked. But if a document relied on one of those leftovers, removal feels like breakage, so those come first.</p>

<div class="post-callout">

  <h3 id="gotchas">Gotchas</h3>

  <ul>
    <li><strong><code class="language-plaintext highlighter-rouge">$message</code> removed from the runtime expression grammar.</strong> AsyncAPI support was dropped before 1.0.0 shipped, but <code class="language-plaintext highlighter-rouge">$message.</code> survived in the ABNF: grammatically legal, resolving to nothing. 1.0.1 removes it. It returns in 1.1.0 with real semantics.</li>
    <li><strong><code class="language-plaintext highlighter-rouge">in: body</code> removed from the Parameter Object.</strong> Request bodies are expressed through the step’s <code class="language-plaintext highlighter-rouge">requestBody</code>, so <code class="language-plaintext highlighter-rouge">in: body</code> had no defined meaning. <code class="language-plaintext highlighter-rouge">in</code> is now <code class="language-plaintext highlighter-rouge">path</code>, <code class="language-plaintext highlighter-rouge">query</code>, <code class="language-plaintext highlighter-rouge">header</code>, or <code class="language-plaintext highlighter-rouge">cookie</code>.</li>
    <li><strong><code class="language-plaintext highlighter-rouge">workflowId</code> references into an external <code class="language-plaintext highlighter-rouge">arazzo</code> source always need the runtime expression form.</strong> 1.0.0 required <code class="language-plaintext highlighter-rouge">$sourceDescriptions.&lt;name&gt;.&lt;workflowId&gt;</code> only when <em>multiple</em> <code class="language-plaintext highlighter-rouge">arazzo</code> source descriptions existed. 1.0.1 requires it whenever the referenced workflow lives in an <code class="language-plaintext highlighter-rouge">arazzo</code> source description at all. Local workflows still use the bare <code class="language-plaintext highlighter-rouge">workflowId</code>. Applies to the Step, Success Action, and Failure Action Objects. Strictly a clarification, but it’s the one that can make a previously accepted document fail a linter.</li>
  </ul>

</div>

<h3 id="changes">Changes</h3>

<ul>
  <li><strong>Step output expressions in the examples are now <code class="language-plaintext highlighter-rouge">$steps.&lt;stepId&gt;.outputs.&lt;name&gt;</code>.</strong> The 1.0.0 grammar always said <code class="language-plaintext highlighter-rouge">.outputs.</code>, but the spec’s own example wrote <code class="language-plaintext highlighter-rouge">$steps.getPetStep.availablePets</code> without it, and people copied the example. Any parser that accepted the short form was matching a spec bug. If your documents use it, they were never valid; fix them whichever version you target.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">retryAfter</code> applies only to <code class="language-plaintext highlighter-rouge">type: retry</code>.</strong> 1.0.0 also mentioned <code class="language-plaintext highlighter-rouge">type: function</code>, which never existed (the allowed types are <code class="language-plaintext highlighter-rouge">end</code>, <code class="language-plaintext highlighter-rouge">retry</code>, and <code class="language-plaintext highlighter-rouge">goto</code>).</li>
</ul>

<h3 id="clarifications">Clarifications</h3>

<ul>
  <li><strong>Examples table gained JSON Pointer fragments</strong> like <code class="language-plaintext highlighter-rouge">$steps.someStepId.outputs.pets#/0/id</code> and <code class="language-plaintext highlighter-rouge">$workflows.foo.outputs.mappedResponse#/name</code>, and the “Workflow output value” note now describes payload fragments the same way step outputs do.</li>
  <li><strong><a href="https://www.rfc-editor.org/rfc/rfc7230">RFC 7230</a> and <a href="https://www.rfc-editor.org/rfc/rfc7231">7231</a> references became <a href="https://www.rfc-editor.org/rfc/rfc9110">RFC 9110</a>.</strong> Request bodies are well-defined for POST, PUT, and PATCH; on GET, HEAD, and DELETE they’re permitted but have no well-defined semantics and should be avoided. Header field names are case-insensitive.</li>
  <li><strong>A lowercase “must” became a normative MUST</strong> in the multi-document section: external documents MUST be included as a Source Description Object.</li>
  <li><strong>Typos, a misplaced parenthesis, link labels, and “runtime expression” capitalized to “Runtime Expression” throughout.</strong> No change in meaning.</li>
</ul>

<h3 id="additions">Additions</h3>

<ul>
  <li><strong>The first official JSON Schema</strong> for Arazzo. Not part of the spec text, but if you validate documents in CI with a generic JSON Schema validator, this is when that became possible. Schemas are published per minor release as dated iterations under <a href="https://spec.openapis.org/arazzo/#schema-iterations">spec.openapis.org/arazzo/</a>, and the latest date within a minor is the correct schema for every patch in it: for 1.0 that is <a href="https://spec.openapis.org/arazzo/1.0/schema/2025-10-15">2025-10-15</a>, which covers both 1.0.0 and 1.0.1 and supersedes the original 2024-12-16 iteration.</li>
</ul>

<h2 id="from-1-0-1-to-1-1-0">From 1.0.1 to 1.1.0: the minor release</h2>

<p>1.1.0 is where the specification grew. It removes no fields, no enum values, and no expression roots. Bump the version string and a 1.0.1 document is a 1.1.0 document. The categories below are ordered by how likely they are to matter to an existing document.</p>

<div class="post-callout">

  <h3 id="gotchas-1">Gotchas</h3>

  <p>A minor release can’t break existing documents, and 1.1.0 removes nothing. Two things can still change how an existing document <em>behaves</em>, because they define what 1.0 left to the implementation:</p>

  <ul>
    <li><strong>Criterion evaluation rules are pinned down further</strong> (see Changes). A runner that previously had to guess at, say, comparisons against <code class="language-plaintext highlighter-rouge">null</code> or string case may now give a different answer. The condition grammar itself is still unspecified, so not every guess went away.</li>
    <li><strong>Forward references are an error in sequential workflows.</strong> If a step references the outputs of a step that appears <em>later</em> in the <code class="language-plaintext highlighter-rouge">steps</code> array, and the workflow doesn’t use <code class="language-plaintext highlighter-rouge">dependsOn</code>, implementations should now reject it.</li>
  </ul>

</div>

<h3 id="additions-1">Additions</h3>

<ul>
  <li><strong>AsyncAPI v3 source descriptions.</strong> <code class="language-plaintext highlighter-rouge">type: asyncapi</code> joins <code class="language-plaintext highlighter-rouge">openapi</code> and <code class="language-plaintext highlighter-rouge">arazzo</code>, and the Step Object gains the fields to talk to an event channel:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">channelPath</code>: a source description plus a JSON Pointer to a channel, for AsyncAPI operations that have no <code class="language-plaintext highlighter-rouge">operationId</code>. Mutually exclusive with <code class="language-plaintext highlighter-rouge">operationId</code> and <code class="language-plaintext highlighter-rouge">workflowId</code>. When the operation does have an <code class="language-plaintext highlighter-rouge">operationId</code>, the spec says to reference it that way instead, the same rule as <code class="language-plaintext highlighter-rouge">operationPath</code> for OpenAPI sources.</li>
      <li><code class="language-plaintext highlighter-rouge">action</code>: <code class="language-plaintext highlighter-rouge">send</code> or <code class="language-plaintext highlighter-rouge">receive</code>.</li>
      <li><code class="language-plaintext highlighter-rouge">correlationId</code>: for <code class="language-plaintext highlighter-rouge">receive</code> steps, which message to wait for. Must match the correlation ID in the AsyncAPI document.</li>
      <li><code class="language-plaintext highlighter-rouge">timeout</code>: milliseconds to wait.</li>
    </ul>

    <p>A <code class="language-plaintext highlighter-rouge">send</code> step completes when the message is sent; Arazzo doesn’t model broker acknowledgment. A <code class="language-plaintext highlighter-rouge">receive</code> step completes when a matching message arrives within the timeout, or fails and triggers <code class="language-plaintext highlighter-rouge">onFailure</code>. With no <code class="language-plaintext highlighter-rouge">successCriteria</code>, any matching message is success. The spec’s own example, trimmed:</p>

    <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">-</span> <span class="na">stepId</span><span class="pi">:</span> <span class="s">confirmOrder</span>
  <span class="na">operationId</span><span class="pi">:</span> <span class="s">$sourceDescriptions.asyncOrderApi.confirmOrder</span>
  <span class="na">action</span><span class="pi">:</span> <span class="s">receive</span>
  <span class="na">correlationId</span><span class="pi">:</span> <span class="s">$inputs.correlationId</span>
  <span class="na">dependsOn</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">placeOrder</span>
  <span class="na">timeout</span><span class="pi">:</span> <span class="m">6000</span>
  <span class="na">outputs</span><span class="pi">:</span>
    <span class="na">orderId</span><span class="pi">:</span> <span class="s">$message.payload.orderId</span>
</code></pre></div>    </div>

    <p>Scope note: 1.1.0 supports AsyncAPI <strong>v3 only</strong>. The final text links to the v3 Operations Object, <code class="language-plaintext highlighter-rouge">send</code>/<code class="language-plaintext highlighter-rouge">receive</code> is v3 terminology, and the only example is <code class="language-plaintext highlighter-rouge">asyncapi: 3.0.0</code>. Nothing addresses v2.</p>
  </li>
  <li>
    <p><strong><a href="https://spec.openapis.org/arazzo/v1.1.0.html#selector-object">Selector Object</a>.</strong> Apply JSONPath, XPath, or JSON Pointer to structured data instead of reaching for a runtime expression with a pointer suffix. <code class="language-plaintext highlighter-rouge">context</code> is a runtime expression that must evaluate to structured data, <code class="language-plaintext highlighter-rouge">selector</code> is the expression, <code class="language-plaintext highlighter-rouge">type</code> is <code class="language-plaintext highlighter-rouge">jsonpath</code>, <code class="language-plaintext highlighter-rouge">xpath</code>, <code class="language-plaintext highlighter-rouge">jsonpointer</code>, or an Expression Type Object:</p>

    <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">outputs</span><span class="pi">:</span>
  <span class="na">firstPetId</span><span class="pi">:</span>
    <span class="na">context</span><span class="pi">:</span> <span class="s">$response.body</span>
    <span class="na">selector</span><span class="pi">:</span> <span class="s">$[0].id</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">jsonpath</span>
</code></pre></div>    </div>

    <p>Allowed anywhere a value used to be just a runtime expression: workflow <code class="language-plaintext highlighter-rouge">outputs</code>, step <code class="language-plaintext highlighter-rouge">outputs</code>, a parameter’s <code class="language-plaintext highlighter-rouge">value</code>, a request body’s <code class="language-plaintext highlighter-rouge">payload</code> (nested at any depth), and a Payload Replacement’s <code class="language-plaintext highlighter-rouge">value</code>. This is the addition I expect to see in the most documents.</p>
  </li>
  <li><strong>Step-level <code class="language-plaintext highlighter-rouge">dependsOn</code>.</strong> A list of <code class="language-plaintext highlighter-rouge">stepId</code>s that must complete before this step runs. Cross-workflow: <code class="language-plaintext highlighter-rouge">$workflows.&lt;workflowId&gt;.steps.&lt;stepId&gt;</code>. Cross-document: <code class="language-plaintext highlighter-rouge">$sourceDescriptions.&lt;name&gt;.&lt;workflowId&gt;.steps.&lt;stepId&gt;</code>. Intended as a join point for in-flight async work; for purely synchronous workflows the recommendation is still to order the array and not use it.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">parameters</code> on Success Action and Failure Action Objects.</strong> An action with a <code class="language-plaintext highlighter-rouge">workflowId</code> can now hand inputs to the workflow it starts. <code class="language-plaintext highlighter-rouge">in</code> must not be used on them.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">$self</code> on the Arazzo Object.</strong> A URI reference that is the document’s canonical identity and the base URI for its relative references. Must not contain a fragment; may itself be relative, resolved against the retrieval URI. If you know JSON Schema, <code class="language-plaintext highlighter-rouge">$self</code> is to an Arazzo document what <code class="language-plaintext highlighter-rouge">$id</code> is to a schema resource.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">$message</code> and <code class="language-plaintext highlighter-rouge">$self</code> as runtime expression roots.</strong> <code class="language-plaintext highlighter-rouge">$message</code> sources are <code class="language-plaintext highlighter-rouge">header.</code>, <code class="language-plaintext highlighter-rouge">query.</code>, <code class="language-plaintext highlighter-rouge">path.</code>, <code class="language-plaintext highlighter-rouge">body</code>, and <code class="language-plaintext highlighter-rouge">payload</code>.</li>
</ul>

<h3 id="extended-functionality">Extended functionality</h3>

<ul>
  <li><strong>Expression Type Object</strong> (renamed from Criterion Expression Type Object, now shared by Criterion and Selector Objects): <code class="language-plaintext highlighter-rouge">type</code> gains <code class="language-plaintext highlighter-rouge">jsonpointer</code>; <code class="language-plaintext highlighter-rouge">version</code> gains <code class="language-plaintext highlighter-rouge">rfc9535</code> (<a href="https://www.rfc-editor.org/rfc/rfc9535">JSONPath</a>), <code class="language-plaintext highlighter-rouge">xpath-31</code> (<a href="https://www.w3.org/TR/xpath-31/">XPath 3.1</a>), and <code class="language-plaintext highlighter-rouge">rfc6901</code> (<a href="https://www.rfc-editor.org/rfc/rfc6901">JSON Pointer</a>), which are now the explicit defaults.</li>
  <li><strong>Payload Replacement Object</strong>: <code class="language-plaintext highlighter-rouge">target</code> may be a JSONPath as well as a JSON Pointer or XPath, and the new <code class="language-plaintext highlighter-rouge">targetSelectorType</code> says which. Defaults: JSON Pointer for <code class="language-plaintext highlighter-rouge">application/json</code>, XPath for XML media types. <code class="language-plaintext highlighter-rouge">value</code> accepts a Selector Object.</li>
  <li><strong>Parameter Object</strong>: <code class="language-plaintext highlighter-rouge">in</code> gains <code class="language-plaintext highlighter-rouge">querystring</code> (the OpenAPI 3.2 concept where the whole query string is one pre-formatted value); <code class="language-plaintext highlighter-rouge">value</code> accepts a Selector Object; <code class="language-plaintext highlighter-rouge">in</code> may be omitted whenever the step, success action, or failure action in context targets a <code class="language-plaintext highlighter-rouge">workflowId</code>.</li>
  <li><strong>Workflow and step <code class="language-plaintext highlighter-rouge">outputs</code></strong> accept a Selector Object as well as a runtime expression.</li>
  <li><strong>Runtime expressions</strong>: <code class="language-plaintext highlighter-rouge">$inputs.</code> and <code class="language-plaintext highlighter-rouge">$outputs.</code> formally allow a <code class="language-plaintext highlighter-rouge">#json-pointer</code> suffix; <code class="language-plaintext highlighter-rouge">$components.</code> covers <code class="language-plaintext highlighter-rouge">successActions</code> and <code class="language-plaintext highlighter-rouge">failureActions</code> alongside <code class="language-plaintext highlighter-rouge">parameters</code>; <code class="language-plaintext highlighter-rouge">$sourceDescriptions.&lt;name&gt;.&lt;x&gt;</code> gets a resolution order (operation or workflow ID first, then a Source Description field such as <code class="language-plaintext highlighter-rouge">url</code> or <code class="language-plaintext highlighter-rouge">type</code>); <code class="language-plaintext highlighter-rouge">{...}</code> embedding is formally defined, with serialization rules (scalars to strings, objects and arrays to JSON, strings as-is).</li>
</ul>

<h3 id="changes-1">Changes</h3>

<ul>
  <li><strong>Criterion evaluation rules.</strong> For <code class="language-plaintext highlighter-rouge">regex</code>, <code class="language-plaintext highlighter-rouge">jsonpath</code>, and <code class="language-plaintext highlighter-rouge">xpath</code> conditions, runtime expressions must be embedded as <code class="language-plaintext highlighter-rouge">{$expr}</code> and substituted before evaluation. Truthiness per type: a simple condition as written, a regex must match, a JSONPath must produce a non-empty nodelist, an XPath uses its effective boolean value. An evaluation error is a failed criterion. Multiple criteria are combined with logical AND. Simple conditions: <code class="language-plaintext highlighter-rouge">null</code> equals only <code class="language-plaintext highlighter-rouge">null</code>, numeric strings coerce to numbers, string comparison is case-insensitive. What 1.1.0 still doesn’t provide is a grammar for simple conditions: the operators are listed, but tokenization and precedence are left to the implementation, unlike runtime expressions, which get a full ABNF. I wrote about <a href="https://vladimirgorej.com/blog/the-one-arazzo-condition-no-tool-can-evaluate-safely/">one 1.0 condition no tool could evaluate safely</a> for exactly this reason; that article proposes a grammar and evaluation semantics, and adding them to the specification is proposed in <a href="https://github.com/OAI/Arazzo-Specification/issues/518">#518</a> and <a href="https://github.com/OAI/Arazzo-Specification/issues/517">#517</a>.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">successCriteria</code>, if present, must contain at least one Criterion Object.</strong></li>
  <li><strong>Schema only:</strong> <code class="language-plaintext highlighter-rouge">retryAfter</code> is no longer required when <code class="language-plaintext highlighter-rouge">type: retry</code>, and a <code class="language-plaintext highlighter-rouge">retry</code> failure action no longer has to name a <code class="language-plaintext highlighter-rouge">stepId</code> or <code class="language-plaintext highlighter-rouge">workflowId</code>. Both bring the schema in line with what the prose always allowed. The 1.1 schema is a separate iteration, <a href="https://spec.openapis.org/arazzo/1.1/schema/2026-04-15">2026-04-15</a>.</li>
</ul>

<h3 id="clarifications-1">Clarifications</h3>

<ul>
  <li><strong>Step dependencies and execution order.</strong> Output references like <code class="language-plaintext highlighter-rouge">$steps.stepId.outputs.field</code> are implicit dependencies and tools must satisfy them; tools must also respect every declared <code class="language-plaintext highlighter-rouge">dependsOn</code>.</li>
  <li><strong>Defining success for asynchronous steps</strong>, as summarized under AsyncAPI above.</li>
  <li><strong>Parsing documents.</strong> A document must be parsed whole before references are resolved; parsing a fragment is undefined behaviour.</li>
  <li><strong>Identity-based referencing.</strong> A reference to another Arazzo document must use its <code class="language-plaintext highlighter-rouge">$self</code> if it has one, and absolute source <code class="language-plaintext highlighter-rouge">url</code>s resolve by identity, not location, so a document already loaded under its <code class="language-plaintext highlighter-rouge">$self</code> isn’t fetched again.</li>
  <li><strong>Base URI resolution</strong>, spelled out: <code class="language-plaintext highlighter-rouge">$self</code>, then the encapsulating entity, then the retrieval URI, then an application default. New Appendix B walks through examples. Relative references in API URLs resolve against the OpenAPI Server Object, not the Arazzo base URI.</li>
  <li><strong>Runtime expression ABNF rewritten</strong> to state formally what the prose already said: <code class="language-plaintext highlighter-rouge">$steps.</code> is <code class="language-plaintext highlighter-rouge">step-id ".outputs." output-name ["#" json-pointer]</code>, <code class="language-plaintext highlighter-rouge">$workflows.</code> likewise, plus a strict identifier form without dots.</li>
  <li><strong>Example fixes</strong>: <code class="language-plaintext highlighter-rouge">$steps.loginUser</code> to <code class="language-plaintext highlighter-rouge">$steps.loginStep</code>, <code class="language-plaintext highlighter-rouge">"{$statusCode == 401}"</code> to <code class="language-plaintext highlighter-rouge">"$statusCode == 401"</code>, JSONPath examples updated to <a href="https://www.rfc-editor.org/rfc/rfc9535">RFC 9535</a>.</li>
</ul>

<h2 id="migration-checklist">A migration checklist</h2>

<p><strong>Moving a document from 1.0.0 to 1.0.1:</strong></p>

<ol>
  <li>Change <code class="language-plaintext highlighter-rouge">arazzo: 1.0.0</code> to <code class="language-plaintext highlighter-rouge">arazzo: 1.0.1</code>.</li>
  <li>Search for <code class="language-plaintext highlighter-rouge">$message.</code> and remove it; there was nothing for it to resolve against.</li>
  <li>Search for <code class="language-plaintext highlighter-rouge">in: body</code> and move that data into the step’s <code class="language-plaintext highlighter-rouge">requestBody</code>.</li>
  <li>Search for <code class="language-plaintext highlighter-rouge">$steps.</code> references missing <code class="language-plaintext highlighter-rouge">.outputs.</code> and add it. These were never valid.</li>
  <li>If any <code class="language-plaintext highlighter-rouge">workflowId</code> points into an external <code class="language-plaintext highlighter-rouge">arazzo</code> source description, write it as <code class="language-plaintext highlighter-rouge">$sourceDescriptions.&lt;name&gt;.&lt;workflowId&gt;</code>, even if there’s only one such source.</li>
</ol>

<p><strong>Moving from 1.0.1 to 1.1.0:</strong></p>

<ol>
  <li>Change the version string. That’s the required part.</li>
  <li>If a runner evaluates your <code class="language-plaintext highlighter-rouge">successCriteria</code>, re-read them against the new condition semantics, especially anything comparing to <code class="language-plaintext highlighter-rouge">null</code> or relying on case.</li>
  <li>Then, optionally, start using what 1.1.0 gives you: Selector Objects in outputs, <code class="language-plaintext highlighter-rouge">parameters</code> on actions, <code class="language-plaintext highlighter-rouge">$self</code> on multi-document setups, and AsyncAPI v3 sources with <code class="language-plaintext highlighter-rouge">dependsOn</code> for the join points.</li>
</ol>

<p>The 1.1.0 release is three months old, and most of the documents I’ve come across still say 1.0.1. That makes now a good time to read the changes, before habits form around the old ones.</p>

<h2 id="sources">Sources</h2>

<ul>
  <li><a href="https://github.com/OAI/Arazzo-Specification/releases/tag/1.0.0">Arazzo 1.0.0</a>, <a href="https://github.com/OAI/Arazzo-Specification/releases/tag/1.0.1">1.0.1</a>, and <a href="https://github.com/OAI/Arazzo-Specification/releases/tag/1.1.0">1.1.0</a> release notes</li>
  <li><a href="https://github.com/OAI/Arazzo-Specification/compare/1.0.0...1.0.1">1.0.0 to 1.0.1 diff</a> and <a href="https://github.com/OAI/Arazzo-Specification/compare/1.0.1...1.1.0">1.0.1 to 1.1.0 diff</a></li>
</ul>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[Three releases in two years. A guide to what each one changed, which changes can trip up an existing document, and what 1.1.0 lets you describe that 1.0 couldn't.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://usearazzo.com/assets/images/blog/arazzo-specification-evolution.png" /><media:content medium="image" url="https://usearazzo.com/assets/images/blog/arazzo-specification-evolution.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">I Went Looking for Everything Arazzo</title><link href="https://usearazzo.com/blog/arazzo-ecosystem/" rel="alternate" type="text/html" title="I Went Looking for Everything Arazzo" /><published>2026-08-22T00:00:00+00:00</published><updated>2026-08-22T00:00:00+00:00</updated><id>https://usearazzo.com/blog/arazzo-ecosystem</id><content type="html" xml:base="https://usearazzo.com/blog/arazzo-ecosystem/"><![CDATA[<p>A couple of days ago I set out to answer a simple question: what exists around Arazzo?</p>

<p>I had a rough picture from working on the specification and on tooling, but a rough picture isn’t a list. So I went looking properly: the OAI repository, vendor blogs, YouTube, GitHub search, Slack threads, package registries, the odd LinkedIn post. I wanted to know what tools there were, who was writing about the spec, who was talking about it, and whether anyone had published a real Arazzo document for a real API.</p>

<p>It took longer than I expected, because nothing was in one place. That’s the normal state of a two-year-old specification, and it’s also the reason I kept notes. Those notes became the <strong><a href="/ecosystem/">Arazzo Ecosystem</a></strong> page.</p>

<h2 id="what-i-found">What I found</h2>

<p>More than I thought, in some places. The page currently lists 24 tools from roughly 16 vendors and independent developers, covering authoring, validation, execution, generation from OpenAPI, and libraries in TypeScript, Python, Go, Java, and PHP. The specification itself is there in all three versions, with its JSON Schemas, official examples, and the places where its development happens.</p>

<p>Articles and talks are a different story: there’s far more out there than fits in a couple of evenings. The eleven articles and four videos listed so far are simply the ones I’ve read or watched myself, and I’d rather list fewer with an accurate one-line description than paste in a hundred links I haven’t opened. That section will grow steadily.</p>

<p>What I genuinely found little of: Arazzo documents published by API providers describing their own APIs, and anything yet on 1.1, which shipped in May with AsyncAPI support.</p>

<p>I’ve kept my opinions off the page on purpose. Each entry gets a one-line description of what it is, not a rating. The tools are grouped by the job they do, so you can find a runner or a linter without knowing vendor names first.</p>

<h2 id="whats-deliberately-not-there">What’s deliberately not there</h2>

<p><a href="https://github.com/usearazzo/arazzo-toolkit">UseArazzo’s own toolkit</a>. The page is for everyone else’s work, including tools that do the same jobs ours do. A registry that left those out wouldn’t be a reference, it would be a brochure, and the Arazzo ecosystem is far too young for that to help anyone. The only competition that matters right now is with “nobody uses this.”</p>

<p>The curation policy is written at the bottom of the page and it’s short: about Arazzo, reachable at a stable URL, not a duplicate. Any vendor, no ranking, free.</p>

<h2 id="its-a-starting-point">It’s a starting point</h2>

<p>This is a first pass. I’ll keep adding things as I find them, and I’ll review the page regularly; the date is shown at the top so you can see how fresh it is.</p>

<p>But I’d rather not be the only one looking. If you’ve built something, written something, recorded something, or published an Arazzo document and it isn’t listed, <a href="https://github.com/usearazzo/website/issues/new?template=submit-a-resource.yml">submit it</a>. The form takes a minute. If something listed is wrong or gone, tell me that too.</p>

<p>If you want to know when the page changes, there’s a <a href="https://github.com/usearazzo/website/commits/main/pages/ecosystem.html.atom">commit feed</a> for now and a newsletter on the way.</p>

<p>The ecosystem is bigger than it looks from any single corner of it. <em>Now there’s a place to see the whole thing.</em></p>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[Now there's a place to see the whole thing.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://usearazzo.com/assets/images/blog/arazzo-ecosystem.png" /><media:content medium="image" url="https://usearazzo.com/assets/images/blog/arazzo-ecosystem.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">API Workflows Are Still Improvised. Here Is What We Are Doing About It</title><link href="https://usearazzo.com/blog/api-workflows-are-still-improvised/" rel="alternate" type="text/html" title="API Workflows Are Still Improvised. Here Is What We Are Doing About It" /><published>2026-08-18T00:00:00+00:00</published><updated>2026-08-18T00:00:00+00:00</updated><id>https://usearazzo.com/blog/api-workflows-are-still-improvised</id><content type="html" xml:base="https://usearazzo.com/blog/api-workflows-are-still-improvised/"><![CDATA[<p>I keep building API tools. This time, I’m building a home for quality Arazzo tooling.</p>

<h2 id="the-gap-openapi-leaves-open">The gap OpenAPI leaves open</h2>

<p>A single API call is a solved problem: OpenAPI describes it, generators wrap it, every HTTP client on earth can make it. But the order you call the endpoints in, what you carry from one response into the next request, and how you know you’re done? That’s been improvised. Scripts, tribal knowledge, agents winging it at runtime. If you maintain an API, test one, or build agents on top of one, you’ve written that glue yourself.</p>

<p>Improvisation is exactly what you don’t want in production. That glue is rarely reviewed, rarely versioned alongside the API it drives, and it breaks quietly when the API changes.</p>

<p>If you haven’t met it yet: <a href="https://spec.openapis.org/arazzo/latest.html">Arazzo</a> is the OpenAPI Initiative’s specification for multi-step API workflows, authored by Frank Kilcommins, and it fixes that on paper. Where OpenAPI answers “what can I call?”, Arazzo answers “in what order, with what, and until when?”. A workflow document lists its source descriptions (usually OpenAPI documents), then a set of workflows made of steps. Each step points at an operation, says which parameters and body to send, declares what counts as success, and names the outputs it makes available to later steps.</p>

<p>A minimal document looks like this:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">arazzo</span><span class="pi">:</span> <span class="s">1.0.1</span>
<span class="na">info</span><span class="pi">:</span>
  <span class="na">title</span><span class="pi">:</span> <span class="s">Place an order</span>
  <span class="na">description</span><span class="pi">:</span> <span class="s">Look a product up by SKU, then order it.</span>
  <span class="na">version</span><span class="pi">:</span> <span class="s">1.0.0</span>
<span class="na">sourceDescriptions</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">shop</span>
    <span class="na">url</span><span class="pi">:</span> <span class="s">./openapi.yaml</span>
    <span class="na">type</span><span class="pi">:</span> <span class="s">openapi</span>
<span class="na">workflows</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">workflowId</span><span class="pi">:</span> <span class="s">placeOrder</span>
    <span class="na">description</span><span class="pi">:</span> <span class="s">Two calls, in order, with the product id carried between them.</span>
    <span class="na">inputs</span><span class="pi">:</span>
      <span class="na">type</span><span class="pi">:</span> <span class="s">object</span>
      <span class="na">properties</span><span class="pi">:</span>
        <span class="na">sku</span><span class="pi">:</span>
          <span class="na">type</span><span class="pi">:</span> <span class="s">string</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">stepId</span><span class="pi">:</span> <span class="s">findProduct</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s">Resolve the SKU to a product id.</span>
        <span class="na">operationId</span><span class="pi">:</span> <span class="s">getProduct</span>
        <span class="na">parameters</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">sku</span>
            <span class="na">in</span><span class="pi">:</span> <span class="s">path</span>
            <span class="na">value</span><span class="pi">:</span> <span class="s">$inputs.sku</span>
        <span class="na">successCriteria</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">$statusCode == </span><span class="m">200</span>
        <span class="na">outputs</span><span class="pi">:</span>
          <span class="na">productId</span><span class="pi">:</span> <span class="s">$response.body#/id</span>
      <span class="pi">-</span> <span class="na">stepId</span><span class="pi">:</span> <span class="s">createOrder</span>
        <span class="na">description</span><span class="pi">:</span> <span class="s">Order the product found in the previous step.</span>
        <span class="na">operationId</span><span class="pi">:</span> <span class="s">createOrder</span>
        <span class="na">requestBody</span><span class="pi">:</span>
          <span class="na">contentType</span><span class="pi">:</span> <span class="s">application/json</span>
          <span class="na">payload</span><span class="pi">:</span>
            <span class="na">productId</span><span class="pi">:</span> <span class="s">$steps.findProduct.outputs.productId</span>
        <span class="na">successCriteria</span><span class="pi">:</span>
          <span class="pi">-</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">$statusCode == </span><span class="m">201</span>
        <span class="na">outputs</span><span class="pi">:</span>
          <span class="na">orderId</span><span class="pi">:</span> <span class="s">$response.body#/id</span>
    <span class="na">outputs</span><span class="pi">:</span>
      <span class="na">orderId</span><span class="pi">:</span> <span class="s">$steps.createOrder.outputs.orderId</span>
</code></pre></div></div>

<p>Nothing here is code. It’s a plain document you can check into the repository next to the OpenAPI description it depends on, review in a pull request, and hand to any tool that understands the specification. If you stop using one toolkit, the workflow keeps working with the next one.</p>

<p>And here is what comes back when you run it. Hand the document and its inputs to the Runner (more on it below) and instead of a script that just ran, you get a record of the run:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">executor</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="dl">'</span><span class="s1">placeOrder</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">inputs</span><span class="p">:</span> <span class="p">{</span> <span class="na">sku</span><span class="p">:</span> <span class="dl">'</span><span class="s1">AZ-1001</span><span class="dl">'</span> <span class="p">}</span> <span class="p">});</span>

<span class="nx">result</span><span class="p">.</span><span class="nx">status</span><span class="p">;</span>   <span class="c1">// 'completed'</span>
<span class="nx">result</span><span class="p">.</span><span class="nx">outputs</span><span class="p">;</span>  <span class="c1">// { orderId: 'ord_8f2c' }</span>
<span class="nx">result</span><span class="p">.</span><span class="nx">steps</span><span class="p">;</span>
<span class="c1">// [</span>
<span class="c1">//   { stepId: 'findProduct', successful: true, action: undefined, attempts: 1, durationMs: 142 },</span>
<span class="c1">//   { stepId: 'createOrder', successful: true, action: undefined, attempts: 1, durationMs: 208 },</span>
<span class="c1">// ]</span>
</code></pre></div></div>

<p>Each step in run order: whether it succeeded, which success or failure action was selected, how many attempts it took, and how long. When the second call fails at 2 a.m., that’s the difference between reading a trace and reading someone’s shell script.</p>

<h2 id="the-problem-isnt-the-spec-its-the-tooling">The problem isn’t the spec, it’s the tooling</h2>

<p>Tooling for Arazzo exists. But it’s scattered across the ecosystem: a validator in one project, execution in another, nothing sharing an engine or a set of conventions. Each tool has to parse the document and resolve references its own way, so they can disagree on the edge cases.</p>

<p>So that’s the mission with UseArazzo: concentrate quality Arazzo tooling in one place, all of it sharing one engine. We’re starting with the three pieces everything else depends on.</p>

<ul>
  <li><strong><a href="/validator/">Validator</a></strong>: every specification violation, pinned to the exact location in the document that caused it, as LSP-compatible diagnostics.</li>
  <li><strong><a href="/runner/">Runner</a></strong>: executes workflows against live APIs described by OpenAPI source descriptions, step by step, and returns the trace you saw above.</li>
  <li><strong><a href="/cli/">CLI</a></strong>: validate and run in one command, on your machine and in CI. A design sketch so far. No package exists yet, and the page says so.</li>
</ul>

<p>Underneath them sit <code class="language-plaintext highlighter-rouge">@usearazzo/parser</code> and <code class="language-plaintext highlighter-rouge">@usearazzo/resolver</code>, the lower-level packages the products are built from, for people building their own Arazzo tooling. That shared engine is the point. When the Validator and the Runner disagree about a document, that’s a bug, not a difference of opinion.</p>

<p>That engine is built on <a href="https://github.com/speclynx">SpecLynx</a>, our own API tooling foundation: the semantic parsing core, reference resolution, and standards discipline that came out of years of Swagger and OpenAPI work. Between the two of us, <a href="/about/#francesco-tumanischvili">Francesco</a> and I were core contributors to the Swagger open-source projects on both the JavaScript and the Java side, and we contribute to the OpenAPI, AsyncAPI, and Arazzo specifications themselves. The <a href="/about/#our-team">About page</a> has the receipts. That’s why the toolkit handles malformed documents, external references, and specification edge cases from day one instead of learning them the hard way.</p>

<p>Three principles guide the work:</p>

<ul>
  <li><strong>Deterministic execution.</strong> A workflow that ran yesterday runs the same today. Agents get autonomy at the decision boundary, not inside the steps.</li>
  <li><strong>The standard is the product.</strong> We implement Arazzo exactly as the specification defines it. When something needs fixing, we fix it in the specification by contributing upstream, instead of adding private extensions only our tools can read.</li>
  <li><strong>Portable artifacts.</strong> Plain Arazzo documents in your repository, driven from your own code, your CI pipeline, or an agent.</li>
</ul>

<h2 id="a-word-of-honesty-before-you-click">A word of honesty before you click</h2>

<p>This is raw. Heavy development, pre-1.0, nothing published to npm yet, APIs changing under our feet. Everything lives in one monorepo, <a href="https://github.com/usearazzo/arazzo-toolkit">usearazzo/arazzo-toolkit</a>, under Apache 2.0.</p>

<p>I’m announcing it anyway. Everything is built in the open, and I’d rather have you watching, questioning, and shaping it from day one than unveil a polished black box a year from now.</p>

<p>Today the toolkit validates and runs Arazzo 1.0.0 and 1.0.1 workflows against OpenAPI 2.0, 3.0.x, and 3.1.x source descriptions. Arazzo 1.1.0 runs partially and doesn’t validate yet, and AsyncAPI isn’t supported. The <a href="/#compatibility">compatibility table</a> on the homepage is where we keep that current, and every product page tells you exactly where that piece stands, including what doesn’t exist yet. No smoke, no mirrors.</p>

<h2 id="where-its-headed">Where it’s headed</h2>

<p>None of this is built. It’s where the toolkit is going, written down so you can tell what exists from what doesn’t.</p>

<p>Next to the three products we want a Language Service, so any editor gets Arazzo validation, completion, and navigation, and an Editor and a VS Code extension built on it. We want GitHub Actions that annotate violations on the diff and render the run trace into the job summary. We want agents to be able to author, validate, and run workflows, through Agent Skills, a generic MCP Server over any document, and an MCP Compiler that turns one workflow into one typed tool. And we want Arazzo Transformers: explicit, versioned transformations for reshaping data between steps, declared as ordinary API calls the toolchain can validate.</p>

<p>We’ll write about each of these here as it moves from idea to code.</p>

<h2 id="follow-along">Follow along</h2>

<ul>
  <li>Star and read the <a href="https://github.com/usearazzo/arazzo-toolkit">arazzo-toolkit</a> repository.</li>
  <li>Tell us what you would need from an Arazzo toolchain in <a href="https://github.com/orgs/usearazzo/discussions">Discussions</a>.</li>
  <li>Subscribe to the <a href="/feed.xml">RSS feed</a> for release notes and deeper posts on how the Validator and Runner work.</li>
  <li>Or just <a href="mailto:info@usearazzo.com">email us</a>.</li>
</ul>

<p>We’re early enough that your input actually shapes the thing.</p>]]></content><author><name>Vladimír Gorej</name></author><summary type="html"><![CDATA[OpenAPI describes endpoints, not the order you call them in. Arazzo fixes that on paper. UseArazzo is building the tooling that makes it real, in the open.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://usearazzo.com/assets/images/blog/api-workflows-are-still-improvised.png" /><media:content medium="image" url="https://usearazzo.com/assets/images/blog/api-workflows-are-still-improvised.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>