Workflows
- What is a workflow?
- Workflow structure
- Workflow inputs
- Workflow outputs
- Global parameters
- Workflow dependencies
- Workflow patterns
- Best practices
Think of workflows as recipes for API operations. Just like a recipe breaks down cooking into steps (chop onions, sauté garlic, add tomatoes), an Arazzo workflow breaks down complex API tasks into manageable steps. If you’ve used GitHub Actions or Jenkins pipelines, the concept will feel familiar, but Arazzo is purpose-built for API orchestration rather than CI/CD.
What is a workflow? #
A workflow is a named sequence of steps that performs a complete task using one or more APIs. Each workflow is self-contained. It declares what inputs it needs, what steps to execute, and what outputs to return. This makes workflows portable and reusable across different contexts.
workflows:
- workflowId: book-train-ticket
summary: Complete workflow for booking a train ticket
description: |
This workflow handles the end-to-end process of booking a train ticket:
1. Search for available trips
2. Select a trip
3. Create a booking
4. Process payment
5. Receive confirmation
inputs:
type: object
properties:
origin:
type: string
destination:
type: string
departureDate:
type: string
format: date
passengers:
type: array
steps:
# ... step definitions
outputs:
bookingId: $steps.createBooking.outputs.bookingId
ticketUrl: $steps.confirm.outputs.ticketUrl
Workflow structure #
Required fields #
Only two fields are actually required to create a workflow:
workflowId identifies the workflow uniquely within the document. Pick something descriptive, which should conform to the regular expression [A-Za-z0-9_\-]+ but why not use kebab-case to make it extra readable.
workflows:
- workflowId: create-and-retrieve-booking
steps is an array of actions to execute. We’ll cover steps in detail later, but for now just know that every workflow needs at least one step.
workflows:
- workflowId: simple-workflow
steps:
- stepId: firstStep
# ... step configuration
- stepId: secondStep
# ... step configuration
Optional fields #
summary - A brief description:
workflows:
- workflowId: book-ticket
summary: Search for and book a train ticket
description - A longer description that can go much longer, even including CommonMark (Markdown) formatting. This is great for documenting complex workflows and will be used by tools to generate nice documentation pages.
workflows:
- workflowId: complex-booking
summary: Multi-step booking process
description: |
This workflow handles complex booking scenarios including:
- Multi-city travel
- Group bookings with different passenger types
- Seat selection and preferences
- Special assistance requests
- Payment plan options
The workflow includes extensive error handling for:
- Sold out trips
- Payment failures
- Booking timeouts
inputs - Define what data the workflow accepts using a JSON Schema object:
workflows:
- workflowId: search-and-book
inputs:
type: object
required:
- origin
- destination
properties:
origin:
type: string
description: Departure station code
example: BOS
destination:
type: string
description: Arrival station code
example: NYC
departureDate:
type: string
format: date
description: Date of travel
outputs - Define what data the workflow produces:
workflows:
- workflowId: book-ticket
outputs:
bookingId:
$steps.createBooking.outputs.bookingId
confirmationCode:
$steps.confirm.outputs.code
totalPrice:
$steps.payment.outputs.amount
parameters - Global parameters that apply to all steps (unless overridden):
workflows:
- workflowId: authenticated-workflow
parameters:
- name: Authorization
in: header
value: Bearer $inputs.token
steps:
# All steps will include this Authorization header
# unless they override it
dependsOn - Specify dependencies on other workflows:
workflows:
- workflowId: authenticated-booking
dependsOn:
- authenticate-user
steps:
# This workflow assumes authentication has been completed
Workflow inputs #
Workflows need some data to get started. Maybe it’s a user ID, search criteria, or a new resource being created. Arazzo uses JSON Schema to define what inputs a workflow accepts, which has the handy side effect of providing validation and documentation automatically.
Basic inputs #
workflows:
- workflowId: search-trips
inputs:
type: object
properties:
origin:
type: string
destination:
type: string
date:
type: string
format: date
Input objects can have any valid JSON Schema structure, including nested objects and arrays. If you are new to JSON Schema, check out the official documentation for a full guide.
Required vs optional #
Some inputs are must-haves (where are you going?), while others can have sensible defaults (how many passengers? probably just one).
The required array specifies which inputs must be provided, everything else is assumed to be optional, with default values where specified:
workflows:
- workflowId: flexible-search
inputs:
type: object
required:
- origin
- destination
properties:
origin:
type: string
destination:
type: string
departureDate:
type: string
format: date
description: Optional - defaults to today
passengers:
type: integer
default: 1
minimum: 1
maximum: 9
Complex input types #
Inputs can be as simple or complex as you need. Nested objects, arrays, enums, JSON Schema has a keyword for pretty much everything.
workflows:
- workflowId: book-with-preferences
inputs:
type: object
properties:
passengers:
type: array
items:
type: object
properties:
name:
type: string
age:
type: integer
seatPreference:
type: string
enum: [window, aisle, any]
paymentMethod:
type: object
properties:
type:
type: string
enum: [credit_card, debit_card, paypal]
token:
type: string
Using inputs in steps #
Once you’ve defined inputs at the workflow level, steps can access them using runtime expressions like $inputs.origin. This is how data flows from the workflow into individual API calls:
workflows:
- workflowId: search-workflow
inputs:
type: object
properties:
origin:
type: string
destination:
type: string
steps:
- stepId: search
operationId: $sourceDescriptions.api.searchTrips
parameters:
- name: origin
in: query
value: $inputs.origin # Reference input
- name: destination
in: query
value: $inputs.destination # Reference input
Workflow outputs #
Workflows should return useful data. What good is running a booking workflow if you can’t get the booking ID back? Outputs define what data the workflow makes available upon completion.
Simple outputs #
workflows:
- workflowId: create-booking
steps:
- stepId: book
# ... creates a booking
outputs:
bookingId: $response.body#/id
outputs:
bookingId: $steps.book.outputs.bookingId
createdAt: $steps.book.outputs.timestamp
Computed outputs #
workflows:
- workflowId: calculate-total
steps:
- stepId: getBasePrice
outputs:
basePrice: $response.body#/price
- stepId: getTax
outputs:
taxAmount: $response.body#/tax
outputs:
basePrice: $steps.getBasePrice.outputs.basePrice
tax: $steps.getTax.outputs.taxAmount
total: $steps.getBasePrice.outputs.basePrice + $steps.getTax.outputs.taxAmount
Global parameters #
Got a header or query parameter that every single step needs? Don’t repeat yourself. Define them at the workflow level and all steps inherit it automatically:
workflows:
- workflowId: api-with-auth
parameters:
# This header will be included in all step requests
- name: Authorization
in: header
value: Bearer $inputs.apiKey
# API version header
- name: API-Version
in: header
value: "2024-01"
steps:
- stepId: getUser
operationId: $sourceDescriptions.api.getUser
# Automatically includes Authorization and API-Version headers
- stepId: updateUser
operationId: $sourceDescriptions.api.updateUser
# Also automatically includes both headers
Workflow dependencies #
When workflows need to run in a specific order, the dependsOn field makes those dependencies explicit. This is particularly useful for authentication flows, multi-stage processes, or any scenario where one workflow produces data that another workflow requires:
workflows:
# Base workflow - no dependencies
- workflowId: authenticate
summary: Get an authentication token
steps:
- stepId: login
outputs:
token: $response.body#/access_token
# Depends on authentication
- workflowId: get-user-bookings
dependsOn:
- authenticate
summary: Retrieve bookings for authenticated user
steps:
- stepId: fetchBookings
parameters:
- name: Authorization
in: header
value: Bearer $inputs.token # Assumes token from authenticate workflow
Smart tools can use this information to automatically run prerequisite workflows, validate execution order, or generate nice workflow diagrams.
Workflow patterns #
Here are some common patterns for organizing workflows:
Linear sequential workflows #
The most straightforward pattern - steps execute one after another:
workflows:
- workflowId: linear-booking
steps:
- stepId: searchTrips
- stepId: selectTrip
- stepId: createBooking
- stepId: processPayment
Multi-source workflows #
Workflows can orchestrate across multiple APIs by referencing operations from different source descriptions:
workflows:
- workflowId: complete-order
steps:
- stepId: checkInventory
operationId: $sourceDescriptions.inventoryApi.checkStock
- stepId: processPayment
operationId: $sourceDescriptions.paymentApi.createCharge
- stepId: scheduleShipping
operationId: $sourceDescriptions.shippingApi.createShipment
Nested workflows #
Break complex processes into smaller, reusable workflows:
workflows:
- workflowId: authenticate
summary: Get authentication token
steps:
- stepId: login
operationId: $sourceDescriptions.authApi.login
- workflowId: authenticated-booking
dependsOn:
- authenticate
steps:
- stepId: getProfile
operationId: $sourceDescriptions.userApi.getProfile
- stepId: createBooking
operationId: $sourceDescriptions.bookingApi.create
Best practices #
Here’s a few tips for writing workflows that are easy to understand and maintain.
Descriptive workflow IDs #
Pick workflow IDs that clearly describe what the workflow does:
# Good - clear and descriptive
- workflowId: search-and-book-train-ticket
- workflowId: cancel-booking-with-refund
- workflowId: update-passenger-details
# Avoid - vague or cryptic
- workflowId: workflow1
- workflowId: process
- workflowId: sab
Write summaries that add value #
Don’t just repeat the workflow ID in different words. Your summary should tell readers what the workflow accomplishes and when they’d want to use it:
# Good
- workflowId: guest-checkout
summary: Complete checkout process for guest users without registration
# Avoid
- workflowId: checkout
summary: Checkout workflow
Document complex workflows #
For workflows with many steps or intricate logic, use the description field to explain the overall process, key decisions, and error handling:
- workflowId: multi-city-booking
summary: Book a multi-city train journey
description: |
This workflow handles the complexity of booking trips across multiple cities:
**Process:**
1. Validates that all cities form a valid route
2. Searches for available trips for each leg
3. Ensures compatible connection times
4. Creates individual bookings for each leg
5. Links bookings together
6. Processes single payment for entire journey
**Error Handling:**
- If any leg is unavailable, the entire booking fails
- If payment fails, all leg bookings are cancelled
- Connection validation ensures minimum 30 minute layovers
Keep workflows focused #
Each workflow should do one thing well. Don’t create a mega-workflow that handles everything from user registration to coffee machine calibration:
# Good - focused workflows
- workflowId: search-trips
- workflowId: create-booking
- workflowId: process-payment
- workflowId: cancel-booking
# Avoid - overly broad workflow
- workflowId: do-everything
# ... handles search, booking, payment, cancellation, refunds, ...
Steps are where the real action happens in a workflow. They define the individual operations that get executed, whether that’s calling an API endpoint, invoking another workflow, or performing conditional logic, so lets get stuck into that.