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.
So while building the UseArazzo toolkit, 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 Validator has to draw the line per version and the Runner has to honour it.
Those notes, tidied up, are this article. It’s for anyone who has to care what the arazzo: line at the top of a document means: people writing the documents, and people writing the tools that read them. If yours still say arazzo: 1.0.0, 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.
Three releases at a glance
| Version | Released | Nature |
|---|---|---|
| 1.0.0 | September 2024 | Initial stable release |
| 1.0.1 | January 2025 | Patch: erratum fixes and clarifications, plus the first official JSON Schema |
| 1.1.0 | May 2026 | Minor: AsyncAPI v3 sources, Selector Objects, step dependencies, document identity, tightened evaluation semantics |
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.
From 1.0.0 to 1.0.1: the patch
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.
Gotchas
$messageremoved from the runtime expression grammar. AsyncAPI support was dropped before 1.0.0 shipped, but$message.survived in the ABNF: grammatically legal, resolving to nothing. 1.0.1 removes it. It returns in 1.1.0 with real semantics.in: bodyremoved from the Parameter Object. Request bodies are expressed through the step’srequestBody, soin: bodyhad no defined meaning.inis nowpath,query,header, orcookie.workflowIdreferences into an externalarazzosource always need the runtime expression form. 1.0.0 required$sourceDescriptions.<name>.<workflowId>only when multiplearazzosource descriptions existed. 1.0.1 requires it whenever the referenced workflow lives in anarazzosource description at all. Local workflows still use the bareworkflowId. 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.
Changes
- Step output expressions in the examples are now
$steps.<stepId>.outputs.<name>. The 1.0.0 grammar always said.outputs., but the spec’s own example wrote$steps.getPetStep.availablePetswithout 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. retryAfterapplies only totype: retry. 1.0.0 also mentionedtype: function, which never existed (the allowed types areend,retry, andgoto).
Clarifications
- Examples table gained JSON Pointer fragments like
$steps.someStepId.outputs.pets#/0/idand$workflows.foo.outputs.mappedResponse#/name, and the “Workflow output value” note now describes payload fragments the same way step outputs do. - RFC 7230 and 7231 references became RFC 9110. 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.
- A lowercase “must” became a normative MUST in the multi-document section: external documents MUST be included as a Source Description Object.
- Typos, a misplaced parenthesis, link labels, and “runtime expression” capitalized to “Runtime Expression” throughout. No change in meaning.
Additions
- The first official JSON Schema 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 spec.openapis.org/arazzo/, and the latest date within a minor is the correct schema for every patch in it: for 1.0 that is 2025-10-15, which covers both 1.0.0 and 1.0.1 and supersedes the original 2024-12-16 iteration.
From 1.0.1 to 1.1.0: the minor release
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.
Gotchas
A minor release can’t break existing documents, and 1.1.0 removes nothing. Two things can still change how an existing document behaves, because they define what 1.0 left to the implementation:
- Criterion evaluation rules are pinned down further (see Changes). A runner that previously had to guess at, say, comparisons against
nullor string case may now give a different answer. The condition grammar itself is still unspecified, so not every guess went away. - Forward references are an error in sequential workflows. If a step references the outputs of a step that appears later in the
stepsarray, and the workflow doesn’t usedependsOn, implementations should now reject it.
Additions
- AsyncAPI v3 source descriptions.
type: asyncapijoinsopenapiandarazzo, and the Step Object gains the fields to talk to an event channel:channelPath: a source description plus a JSON Pointer to a channel, for AsyncAPI operations that have nooperationId. Mutually exclusive withoperationIdandworkflowId. When the operation does have anoperationId, the spec says to reference it that way instead, the same rule asoperationPathfor OpenAPI sources.action:sendorreceive.correlationId: forreceivesteps, which message to wait for. Must match the correlation ID in the AsyncAPI document.timeout: milliseconds to wait.
A
sendstep completes when the message is sent; Arazzo doesn’t model broker acknowledgment. Areceivestep completes when a matching message arrives within the timeout, or fails and triggersonFailure. With nosuccessCriteria, any matching message is success. The spec’s own example, trimmed:- stepId: confirmOrder operationId: $sourceDescriptions.asyncOrderApi.confirmOrder action: receive correlationId: $inputs.correlationId dependsOn: - placeOrder timeout: 6000 outputs: orderId: $message.payload.orderIdScope note: 1.1.0 supports AsyncAPI v3 only. The final text links to the v3 Operations Object,
send/receiveis v3 terminology, and the only example isasyncapi: 3.0.0. Nothing addresses v2. -
Selector Object. Apply JSONPath, XPath, or JSON Pointer to structured data instead of reaching for a runtime expression with a pointer suffix.
contextis a runtime expression that must evaluate to structured data,selectoris the expression,typeisjsonpath,xpath,jsonpointer, or an Expression Type Object:outputs: firstPetId: context: $response.body selector: $[0].id type: jsonpathAllowed anywhere a value used to be just a runtime expression: workflow
outputs, stepoutputs, a parameter’svalue, a request body’spayload(nested at any depth), and a Payload Replacement’svalue. This is the addition I expect to see in the most documents. - Step-level
dependsOn. A list ofstepIds that must complete before this step runs. Cross-workflow:$workflows.<workflowId>.steps.<stepId>. Cross-document:$sourceDescriptions.<name>.<workflowId>.steps.<stepId>. 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. parameterson Success Action and Failure Action Objects. An action with aworkflowIdcan now hand inputs to the workflow it starts.inmust not be used on them.$selfon the Arazzo Object. 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,$selfis to an Arazzo document what$idis to a schema resource.$messageand$selfas runtime expression roots.$messagesources areheader.,query.,path.,body, andpayload.
Extended functionality
- Expression Type Object (renamed from Criterion Expression Type Object, now shared by Criterion and Selector Objects):
typegainsjsonpointer;versiongainsrfc9535(JSONPath),xpath-31(XPath 3.1), andrfc6901(JSON Pointer), which are now the explicit defaults. - Payload Replacement Object:
targetmay be a JSONPath as well as a JSON Pointer or XPath, and the newtargetSelectorTypesays which. Defaults: JSON Pointer forapplication/json, XPath for XML media types.valueaccepts a Selector Object. - Parameter Object:
ingainsquerystring(the OpenAPI 3.2 concept where the whole query string is one pre-formatted value);valueaccepts a Selector Object;inmay be omitted whenever the step, success action, or failure action in context targets aworkflowId. - Workflow and step
outputsaccept a Selector Object as well as a runtime expression. - Runtime expressions:
$inputs.and$outputs.formally allow a#json-pointersuffix;$components.coverssuccessActionsandfailureActionsalongsideparameters;$sourceDescriptions.<name>.<x>gets a resolution order (operation or workflow ID first, then a Source Description field such asurlortype);{...}embedding is formally defined, with serialization rules (scalars to strings, objects and arrays to JSON, strings as-is).
Changes
- Criterion evaluation rules. For
regex,jsonpath, andxpathconditions, runtime expressions must be embedded as{$expr}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:nullequals onlynull, 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 one 1.0 condition no tool could evaluate safely for exactly this reason; that article proposes a grammar and evaluation semantics, and adding them to the specification is proposed in #518 and #517. successCriteria, if present, must contain at least one Criterion Object.- Schema only:
retryAfteris no longer required whentype: retry, and aretryfailure action no longer has to name astepIdorworkflowId. Both bring the schema in line with what the prose always allowed. The 1.1 schema is a separate iteration, 2026-04-15.
Clarifications
- Step dependencies and execution order. Output references like
$steps.stepId.outputs.fieldare implicit dependencies and tools must satisfy them; tools must also respect every declareddependsOn. - Defining success for asynchronous steps, as summarized under AsyncAPI above.
- Parsing documents. A document must be parsed whole before references are resolved; parsing a fragment is undefined behaviour.
- Identity-based referencing. A reference to another Arazzo document must use its
$selfif it has one, and absolute sourceurls resolve by identity, not location, so a document already loaded under its$selfisn’t fetched again. - Base URI resolution, spelled out:
$self, 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. - Runtime expression ABNF rewritten to state formally what the prose already said:
$steps.isstep-id ".outputs." output-name ["#" json-pointer],$workflows.likewise, plus a strict identifier form without dots. - Example fixes:
$steps.loginUserto$steps.loginStep,"{$statusCode == 401}"to"$statusCode == 401", JSONPath examples updated to RFC 9535.
A migration checklist
Moving a document from 1.0.0 to 1.0.1:
- Change
arazzo: 1.0.0toarazzo: 1.0.1. - Search for
$message.and remove it; there was nothing for it to resolve against. - Search for
in: bodyand move that data into the step’srequestBody. - Search for
$steps.references missing.outputs.and add it. These were never valid. - If any
workflowIdpoints into an externalarazzosource description, write it as$sourceDescriptions.<name>.<workflowId>, even if there’s only one such source.
Moving from 1.0.1 to 1.1.0:
- Change the version string. That’s the required part.
- If a runner evaluates your
successCriteria, re-read them against the new condition semantics, especially anything comparing tonullor relying on case. - Then, optionally, start using what 1.1.0 gives you: Selector Objects in outputs,
parameterson actions,$selfon multi-document setups, and AsyncAPI v3 sources withdependsOnfor the join points.
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.
Sources
- Arazzo 1.0.0, 1.0.1, and 1.1.0 release notes
- 1.0.0 to 1.0.1 diff and 1.0.1 to 1.1.0 diff