Agent Surface

API Surface

Arazzo Workflows

Using the OpenAPI Arazzo specification to define multi-step agent workflows with explicit sequencing, control flow, and runtime expressions

Last verified 2026-09-24

Summary

Arazzo defines multi-step workflows as executable specifications, preventing agents from inventing their own order. A workflow document declares the sequence of operations, runtime expressions for data flow between steps, and success/failure handlers. This keeps the sequence deterministic and prevents hallucinated steps or omitted operations in complex tasks like flight booking or payment processing.

  • Latest published specification: Arazzo 1.1.0.
  • Arazzo references one or many (and, since 1.1.0, AsyncAPI) specs and maps them into workflows
  • Each step binds to an operationId and passes data between steps via runtime expressions
  • 1.1.0 adds a Selector Object for extracting a value from a JSON or XML response with JSONPath, XPath, or JSON Pointer
  • Control flow primitives: retry, goto recovery step, end
  • Prevents agent improvisation when sequencing matters
  • Explicit parameter and input component definitions reduce hallucination

Agents improvise. Without explicit workflow definitions, an agent that needs to book a flight will invent its own sequence - searching for flights, selecting one, entering passenger details, processing payment - based on what seems reasonable. It will get the order wrong, skip steps, or hallucinate operations that do not exist.

Arazzo solves this by letting you define the sequence explicitly, in a machine-readable format that agent frameworks can execute directly.

What Arazzo Is

Arazzo is a specification from the OpenAPI Initiative that describes multi-step interaction sequences as executable workflows. It is a companion to OpenAPI 3.x: where OpenAPI describes individual operations, Arazzo describes how those operations compose into business workflows.

A workflow definition includes:

  • Which OpenAPI specs it references (one or many)
  • The ordered steps in the workflow, each mapped to an operationId
  • Runtime expressions for passing data between steps
  • Success and failure actions (retry, goto a recovery step, end)
  • Reusable parameter and input components

Core Structure

An Arazzo document has five top-level fields: arazzo, info, sourceDescriptions, workflows, and components.

arazzo: "1.1.0"
info:
  title: Flight Booking Workflows
  version: "1.0.0"
  description: Multi-step workflows for booking and managing flights

sourceDescriptions:
  - name: flights-api
    url: https://api.example.com/openapi.yaml
    type: openapi
  - name: payments-api
    url: https://payments.example.com/openapi.yaml
    type: openapi

workflows:
  - workflowId: book-flight
    summary: Complete flight booking from search to confirmation
    description: >
      Searches for available flights, selects a seat, collects passenger
      details, processes payment, and returns a booking confirmation.
      Use this workflow when a user wants to book a new flight end-to-end.
    inputs:
      type: object
      required: [origin, destination, departure_date, passenger]
      properties:
        origin:
          type: string
          description: IATA airport code for the departure airport
        destination:
          type: string
          description: IATA airport code for the destination airport
        departure_date:
          type: string
          format: date
        passenger:
          $ref: "#/components/inputs/PassengerDetails"
    steps:
      - stepId: search-flights
        operationId: $sourceDescriptions.flights-api#search_flights
        parameters:
          - name: origin
            in: query
            value: $inputs.origin
          - name: destination
            in: query
            value: $inputs.destination
          - name: date
            in: query
            value: $inputs.departure_date
        successCriteria:
          - condition: $statusCode == 200
          - condition: $response.body#/flights/length > 0
            context: $response.body
            type: jsonpath
        onSuccess:
          - name: proceed-to-select
            type: goto
            stepId: select-flight
        onFailure:
          - name: no-flights-found
            type: end
        outputs:
          flights: $response.body#/flights

      - stepId: select-flight
        operationId: $sourceDescriptions.flights-api#hold_flight
        requestBody:
          contentType: application/json
          payload:
            flight_id: $steps.search-flights.outputs.flights[0].id
            passenger_count: 1
        successCriteria:
          - condition: $statusCode == 200
        onSuccess:
          - name: proceed-to-payment
            type: goto
            stepId: process-payment
        outputs:
          hold_id: $response.body#/hold_id
          total_price: $response.body#/total_price
          currency: $response.body#/currency

      - stepId: process-payment
        operationId: $sourceDescriptions.payments-api#charge_card
        requestBody:
          contentType: application/json
          payload:
            amount: $steps.select-flight.outputs.total_price
            currency: $steps.select-flight.outputs.currency
            description: "Flight booking"
        successCriteria:
          - condition: $statusCode == 200
        onSuccess:
          - name: confirm-booking
            type: goto
            stepId: confirm-booking
        onFailure:
          - name: payment-failed
            type: goto
            stepId: release-hold
        outputs:
          payment_id: $response.body#/payment_id

      - stepId: confirm-booking
        operationId: $sourceDescriptions.flights-api#confirm_flight_booking
        requestBody:
          contentType: application/json
          payload:
            hold_id: $steps.select-flight.outputs.hold_id
            payment_id: $steps.process-payment.outputs.payment_id
            passenger: $inputs.passenger
        successCriteria:
          - condition: $statusCode == 201
        onSuccess:
          - name: booking-complete
            type: end
        outputs:
          confirmation_number: $response.body#/confirmation_number
          booking_id: $response.body#/id

      - stepId: release-hold
        operationId: $sourceDescriptions.flights-api#cancel_flight_hold
        parameters:
          - name: hold_id
            in: path
            value: $steps.select-flight.outputs.hold_id
        onSuccess:
          - name: payment-failed-end
            type: end

components:
  inputs:
    PassengerDetails:
      type: object
      required: [first_name, last_name, email]
      properties:
        first_name:
          type: string
        last_name:
          type: string
        email:
          type: string
          format: email
        date_of_birth:
          type: string
          format: date

Each sourceDescriptions entry's type is openapi, asyncapi, or arazzo (a nested reference to another Arazzo document). The asyncapi type, added in 1.1.0, lets a workflow step bind to an AsyncAPI operation - orchestrating a publish/subscribe or messaging step alongside REST steps in the same workflow, using the same $sourceDescriptions.<name>#<operationId> reference syntax.

Runtime Expressions

Runtime expressions are how Arazzo passes data between steps. They use a $-prefixed path syntax that resolves at execution time.

ExpressionResolves To
$inputs.field_nameA workflow input field
$steps.stepId.outputs.fieldAn output field from a named step
$steps.stepId.request.body#/pathA field from the request body of a named step
$response.body#/json/pointerA JSON Pointer into the current step's response body
$response.body#/fieldShorthand JSON Pointer for top-level response fields
$statusCodeThe HTTP status code of the current step's response
$request.header.nameA header from the current step's request
$response.header.nameA header from the current step's response

Output Extraction

Steps can explicitly declare their outputs to make downstream references cleaner:

steps:
  - stepId: create-order
    operationId: create_order
    requestBody:
      contentType: application/json
      payload:
        product_id: $inputs.product_id
        quantity: $inputs.quantity
    outputs:
      order_id: $response.body#/id
      total: $response.body#/total_price
      status: $response.body#/status

Downstream steps then reference $steps.create-order.outputs.order_id rather than navigating the raw response body repeatedly.

Selector Object (Arazzo 1.1.0)

For extraction that a plain runtime expression can't reach - a JSONPath query, an XPath into an XML body - 1.1.0 adds a Selector Object with three required fields: context (the runtime expression identifying the data source, e.g. $response.body), selector (the query itself), and type (jsonpath, xpath, or jsonpointer):

outputs:
  first_item_id:
    context: $response.body
    selector: $.items[0].id
    type: jsonpath

Use a Selector Object where the plain $response.body#/path shorthand can't express the query - array filtering, XML namespaces, or a JSON Pointer built from a runtime value.

Criterion objects in successCriteria use the same context and type fields but carry the expression in condition, not selector.

Control Flow

Success Actions

After a step succeeds (status code matches successCriteria), you can:

  • end - terminate the workflow, returning any declared outputs
  • goto - jump to another step by stepId
onSuccess:
  - name: order-created
    type: goto
    stepId: send-confirmation-email
  # or:
  - name: workflow-complete
    type: end

Failure Actions

After a step fails, you can:

  • end - terminate the workflow with failure
  • goto - jump to a recovery or cleanup step
  • retry - retry the current step, optionally with a delay and max attempt count
onFailure:
  - name: retry-on-timeout
    type: retry
    retryAfter: 2 # seconds
    retryLimit: 3
    criteria:
      - condition: $statusCode == 503
  - name: goto-cleanup
    type: goto
    stepId: rollback-transaction
    criteria:
      - condition: $statusCode >= 500
  - name: fail-fast
    type: end
    criteria:
      - condition: $statusCode == 403

Success Criteria

successCriteria defines what constitutes a successful step. Default is $statusCode == 200. You can add additional conditions:

successCriteria:
  - condition: $statusCode == 200
  - condition: $response.body#/status == 'confirmed'
  - condition: $response.body#/items/length > 0
    context: $response.body
    type: jsonpath

Multi-Document Support

Arazzo workflows can orchestrate across multiple OpenAPI specs by listing each spec in sourceDescriptions:

sourceDescriptions:
  - name: inventory-api
    url: https://inventory.example.com/openapi.yaml
    type: openapi
  - name: billing-api
    url: https://billing.example.com/openapi.yaml
    type: openapi
  - name: notification-api
    url: https://notifications.example.com/openapi.yaml
    type: openapi

Step operationId references are then prefixed with the source description name:

steps:
  - stepId: check-stock
    operationId: $sourceDescriptions.inventory-api#check_inventory
  - stepId: create-invoice
    operationId: $sourceDescriptions.billing-api#create_invoice
  - stepId: notify-customer
    operationId: $sourceDescriptions.notification-api#send_email

This enables workflows that span service boundaries within a single definition, rather than requiring each service to implement cross-service coordination logic.

Reusable Components

Repeat parameters and input schemas can be extracted into components for reuse across workflows:

components:
  parameters:
    PaginationParameters:
      - name: page
        in: query
        value: $inputs.page
      - name: limit
        in: query
        value: $inputs.limit

  inputs:
    DateRangeInput:
      type: object
      required: [start_date, end_date]
      properties:
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date

Reference them in workflows:

steps:
  - stepId: list-transactions
    operationId: list_transactions
    parameters:
      $ref: "#/components/parameters/PaginationParameters"

How Arazzo Complements MCP

tools expose individual operations. Arazzo describes how those operations combine into business workflows. They are complementary layers:

LayerToolScope
Individual operationsMCP toolsSingle API call
Business workflowsArazzoMulti-step sequences with control flow
OrchestrationAgentHigh-level task completion

When you generate MCP tools from an API, generate tools for both individual endpoints and Arazzo workflows. An agent exposed to a book_flight workflow tool can invoke the complete booking sequence as a single atomic operation, rather than being responsible for sequencing search → hold → pay → confirm itself.

// MCP tool generated from an Arazzo workflow
{
  "name": "book_flight",
  "description": "Books a flight end-to-end: searches availability, holds a seat, processes payment, and returns a confirmation number. Use when a user wants to complete a full flight booking. Requires origin, destination, departure date, and passenger details.",
  "inputSchema": {
    "type": "object",
    "required": ["origin", "destination", "departure_date", "passenger"],
    "properties": {
      "origin": { "type": "string", "description": "IATA code for departure airport" },
      "destination": { "type": "string", "description": "IATA code for destination airport" },
      "departure_date": { "type": "string", "format": "date" },
      "passenger": {
        "type": "object",
        "required": ["first_name", "last_name", "email"],
        "properties": {
          "first_name": { "type": "string" },
          "last_name": { "type": "string" },
          "email": { "type": "string", "format": "email" }
        }
      }
    }
  }
}

Agents improvising multi-step workflows is one of the most common failure modes in production agent systems. If your API requires more than one operation to complete a user-facing action, define an Arazzo workflow for it.

Practical Integration

Generate MCP Tools from Arazzo

When you have an Arazzo spec alongside your OpenAPI spec, generate MCP tools from both:

  • Individual operation tools - one tool per operationId for fine-grained access
  • Workflow tools - one tool per Arazzo workflowId for composite operations

The workflow tool's description comes from the Arazzo workflow.description field; write it as an agent trigger condition (see Naming and Descriptions).

Validation

Validate Arazzo documents with the @jentic/arazzo-validator package before generating tools:

npx @jentic/arazzo-validator validate booking-workflows.arazzo.yaml

Version Alongside OpenAPI

Keep your Arazzo spec in the same repository as your OpenAPI spec and version them together. When you change an operationId in your OpenAPI spec, update all Arazzo references to that operation at the same time.

Checklist

  • Each multi-step user-facing action has a corresponding Arazzo workflow
  • All operationId references in workflows match actual operationId values in the referenced OpenAPI specs
  • Every step has defined onFailure actions for non-200 responses
  • Runtime expression paths have been tested against actual response bodies
  • Workflow descriptions are written as agent trigger conditions
  • Composite MCP tools are generated from workflow definitions

On this page