Zuplo
OpenAPI

Arazzo Goes Cross-Protocol

Martyn DaviesMartyn Davies
May 21, 2026
6 min read

Version 1.1 ships AsyncAPI as a first-class source description, workflow-to-workflow chaining, and a Selector Object for pulling values out of responses. What started as an OpenAPI sidekick has graduated into a cross-protocol workflow spec.

Arazzo 1.1 reaches across protocols: it describes workflows that span REST and event-driven systems in one document, lets one workflow call another with typed inputs, and gives you a proper way to extract values from responses without string templating.

If you’ve only met Arazzo in passing, it’s the OpenAPI Initiative’s spec for sequences of API calls. Where OpenAPI describes individual endpoints, Arazzo describes the order you call them in: “log in, then create a cart, then add items, then check out.”

A document lists one or more source descriptions (the underlying specs that define the operations it references) and a list of workflows made up of steps. Each step points at a real operation, captures inputs and outputs, and decides what happens next based on success criteria. 1.0 only knew about OpenAPI sources; 1.1 widens the lens.

Arazzo documents are static YAML or JSON; a runner reads the document and executes the steps. Tooling for 1.1 is still catching up, so the first useful consumers are SDK generators, test harnesses, AI agents, and developer portals.

Best for:
  • Teams describing APIs beyond endpoint contracts
  • Platforms mixing REST and event-driven systems (Kafka, MQTT, AMQP)
  • Anyone evaluating Arazzo for SDK, test, or AI agent flows

AsyncAPI joins OpenAPI as a source description

The sourceDescriptions array, which used to accept only openapi and arazzo entries, now accepts asyncapi too:

YAMLyaml
arazzo: "1.1.0"
info:
  title: Order Workflow
  version: "1.0"
sourceDescriptions:
  - name: orders
    url: ./orders.openapi.yaml
    type: openapi
  - name: events
    url: ./orders.asyncapi.yaml
    type: asyncapi

With multiple source descriptions in play, steps reference operations using a runtime expression: $sourceDescriptions.<name>.<operationId>. An AsyncAPI operation defined in the events source above is reached as $sourceDescriptions.events.publishOrderCreated. Steps that reference an AsyncAPI operation get three new fields tuned for message flows:

  • action: either send or receive, so the step knows whether it’s publishing a message or waiting for one.
  • correlationId: the value the workflow uses to match a published request to its eventual response (AsyncAPI’s existing correlation primitive, surfaced into Arazzo).
  • timeout: how long, in milliseconds, the runtime should wait for a receive step before failing the workflow.
YAMLyaml
- stepId: publishOrderCreated
  operationId: $sourceDescriptions.events.publishOrderCreated
  action: send
  parameters:
    - name: orderId
      in: header
      value: $steps.createOrder.outputs.id
- stepId: awaitOrderConfirmed
  operationId: $sourceDescriptions.events.subscribeOrderConfirmed
  action: receive
  correlationId: $steps.publishOrderCreated.outputs.correlationId
  timeout: 30000
  successCriteria:
    - condition: $message.payload.status == "confirmed"

A checkout flow where Stripe confirms payment over a webhook, then an order service emits an order.created event onto Kafka before the warehouse picks it up: that’s the shape Arazzo 1.0 couldn’t describe in one document.

1.1 lets you describe both halves in a single workflow. The REST call and the event that follows it live side by side, and the link between them, which response value matches which event payload, is declared in the spec itself rather than explained in a comment or a wiki page.

Chained workflows now take typed inputs

Arazzo 1.0 already let Success and Failure Actions hand off to another workflow via workflowId. What 1.1 adds is a parameters array on actions that reference a workflowId, so the called workflow receives typed inputs instead of inheriting whatever happens to be in scope:

YAMLyaml
workflows:
  - workflowId: refreshAuthIfExpired
    inputs:
      type: object
      properties:
        refreshToken:
          type: string
    steps:
      - stepId: refresh
        operationId: refreshToken
        # ...

  - workflowId: placeOrder
    steps:
      - stepId: createOrder
        operationId: createOrder
        onFailure:
          - name: refreshThenRetry
            type: retry
            workflowId: refreshAuthIfExpired
            parameters:
              - name: refreshToken
                value: $inputs.refreshToken
            criteria:
              - condition: $statusCode == 401

The spec is precise about how the two action types behave:

ActionBehaviourUse it for
type: gotoOne-way transfer to the named workflow. Control does not come back.Branching to a different flow on a condition.
type: retryRuns the named workflow, then returns to retry the failed step.Token refresh, rate-limit backoff.

With those two primitives you can extract recurring patterns like reauthentication or rate-limit backoff into their own workflows and call them from anywhere they apply, instead of duplicating recovery steps inside every flow.

Selector Object unifies JSONPath, XPath, and JSON Pointer

Arazzo 1.0 already supported JSONPath and XPath inside Criterion Objects, so success criteria could match expressions like $.customers[?(@.primary == true)].email. What it lacked was a way to use those same expressions when pulling values out of a response into step outputs, workflow outputs, request bodies, parameter values, or payload replacements. Those extraction points were JSON Pointer only, which falls over the moment you need a JSONPath filter (“the customer where primary is true”) or XPath against an XML response.

1.1 introduces the Selector Object to cover jsonpath, xpath, and jsonpointer with one shape, wherever extraction happens:

YAMLyaml
outputs:
  customerEmail:
    selector:
      context: $response.body
      type: jsonpath
      selector: "$.customers[?(@.primary == true)].email"
  legacyCustomerId:
    selector:
      context: $response.body
      type: xpath
      selector: "/customer/@id"

One mechanism for “reach into this response and grab the bit I need,” instead of templating brittle path strings.

Expression Type Object pins JSONPath and XPath versions

Arazzo 1.1 renames the old Criterion Expression Type Object to the Expression Type Object and extends it to cover selectors as well as conditions. You can also pin the exact expression syntax:

  • JSONPath: rfc9535 or draft-goessner-dispatch-jsonpath-00
  • XPath: xpath-31, xpath-30, xpath-20, xpath-10
  • JSON Pointer: rfc6901
YAMLyaml
selector:
  context: $response.body
  type:
    type: jsonpath
    version: rfc9535
  selector: "$.items[*].sku"

The difference between Goessner’s original JSONPath draft and the formalised RFC 9535 is more than cosmetic. Pinning the version means a workflow runner can refuse to execute a document it can’t evaluate safely, instead of silently doing the wrong thing.

Other Arazzo 1.1 improvements

  • in: querystring on Parameter Objects, aligning with OpenAPI 3.2. Lets you map an entire query string as one value instead of declaring each parameter separately.
  • $self at the document root: a self-assigned URI that anchors relative references and makes cross-document resolution unambiguous when you’re working across a fleet of Arazzo files.
  • Formal ABNF grammar for runtime expressions, plus explicit truthy/falsy semantics for success criteria. Moves “what does this actually evaluate to?” out of the implementer’s head and into the spec.
  • Source description resolution ordering is now formally defined, so two conformant runners should resolve the same reference the same way.

If you’re the person writing the runner, these are the difference between guessing and following the spec.

Arazzo roadmap: gRPC, GraphQL, MCP, A2A

What I’m most excited about isn’t in 1.1 yet, it’s on the roadmap. The release notes flag gRPC, GraphQL, SOAP, plus MCP (Anthropic’s Model Context Protocol, the tool-calling layer agents like Claude and Cursor speak) and A2A (the Agent-to-Agent protocol, originally from Google, now under the Linux Foundation). On top of that: actor-in-loop capabilities, transformers, functions, and loops.

Arazzo’s ambition is to be the workflow description format for any protocol worth scripting against. MCP and A2A on the roadmap is the clearest signal yet that agent frameworks are moving from “let the LLM figure it out” to “give the LLM a machine-readable workflow it can follow.” That’s the part I’ll be watching closely.

Why Arazzo 1.1 matters for gateway teams

A machine-readable workflow spec has the same value as machine-readable endpoints: something else can read it and do useful work. SDK generators, test harnesses, AI agents, and developer portals can all consume an Arazzo document and know how endpoints fit together, not just what each one returns.

With 1.1, that same document can describe the event-driven half of your platform and chain reusable sub-flows, which moves Arazzo from “interesting-on-paper” to a serious candidate for production documentation.

For the longer “what is Arazzo and why does it exist” framing, our overview of Arazzo and OpenAPI Overlay walks through both specifications together. For a hands-on introduction to authoring Arazzo documents, Hunter Skrasek’s API workflows post is the quickest route in. The full Arazzo 1.1 specification is on the OpenAPI Initiative site.