diff --git a/learn/arazzo/practical-example-series/api-contract-testing-01.md b/learn/arazzo/practical-example-series/api-contract-testing-01.md
new file mode 100644
index 00000000..83cd2f3d
--- /dev/null
+++ b/learn/arazzo/practical-example-series/api-contract-testing-01.md
@@ -0,0 +1,720 @@
+# Respect Practical Example Series [01]
+
+## API Contract Testing with Respect
+
+This article shows how to use Respect, powered by Arazzo workflows, for API contract testing. You will learn how to describe an API workflow, execute it with Redocly CLI, and use the results to find mismatches between an OpenAPI description and the real API behavior.
+
+You will cover the following topics:
+
+- Practical applications of Arazzo:
+ - Automating repetitive API workflows.
+ - Adding API contract tests to CI/CD routines.
+ - Keeping API documentation synchronized with actual API behavior.
+ - Sharing described workflows across teams.
+- Using the open-source [@redocly/cli](https://www.npmjs.com/package/@redocly/cli) `respect` command to execute Arazzo workflows.
+
+### What you will build
+
+You will create a simple Arazzo workflow that calls the `GET /menu` operation from an OpenAPI description. Then you will run the workflow with Respect, inspect the contract test results, and fix an intentional mismatch between the OpenAPI description and the actual API response.
+
+### The problem
+
+Many teams test APIs by writing test code in the same language as the API implementation. This can work well for a single service, but it becomes harder to maintain in systems with multiple products, repositories, and programming languages.
+
+Another common challenge is keeping API documentation aligned with actual API behavior. Developers may update an endpoint but forget to update its OpenAPI description, or the documentation may change without the implementation following it. Over time, these small differences make it harder for QA engineers, developers, and technical writers to trust the API contract.
+
+Respect helps address this problem by executing Arazzo workflows against a running API and validating the responses against the connected OpenAPI description. This makes API contract testing more declarative and easier to share across teams.
+
+### Prerequisites
+
+To follow the examples in this article, you need:
+
+- Familiarity with [Arazzo](../what-is-arazzo.md).
+- An API described with OpenAPI. The examples use a modified version of the Redocly Cafe API.
+ IMPORTANT: the API description intentionally contains discrepancies for demonstration purposes.
+
+The example uses the following OpenAPI description. The important part for this article is the `GET /menu` operation and its `MenuItemList` response schema.
+
+
+OpenAPI description used in this example
+
+```yaml
+openapi: 3.1.0
+info:
+ title: Redocly Cafe
+ description: |
+ Demo API for cafe operators (not customers) to manage menus.
+ Create API credentials and try it yourself in a realistic OpenAPI workflow.
+ version: 1.0.0
+ contact:
+ email: team@redocly.com
+ url: https://redocly.com/contact-us/
+ license:
+ name: MIT
+ url: https://opensource.org/licenses/MIT
+ termsOfService: https://redocly.com/subscription-agreement
+servers:
+ - url: https://cafe.cloud.redocly.com
+ description: Live server.
+tags:
+ - name: Authorization
+ description: Create a client to demo the API.
+ - name: Products
+ description: Operations related to products.
+paths:
+ /menu:
+ get:
+ tags:
+ - Products
+ summary: List all menu items
+ description: Retrieve a collection of menu items with optional filtering and pagination.
+ operationId: listMenuItems
+ security: []
+ parameters:
+ - $ref: '#/components/parameters/After'
+ - $ref: '#/components/parameters/Before'
+ - $ref: '#/components/parameters/Sort'
+ - $ref: '#/components/parameters/Filter'
+ - $ref: '#/components/parameters/Search'
+ - $ref: '#/components/parameters/Limit'
+ responses:
+ '200':
+ description: Successful operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MenuItemList'
+ '400':
+ $ref: '#/components/responses/BadRequest'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
+components:
+ parameters:
+ After:
+ name: after
+ in: query
+ required: false
+ description: Use the `endCursor` as a value for the `after` parameter to get the next page.
+ schema:
+ type: string
+ example: a25fgaksjf23la==
+ Before:
+ name: before
+ in: query
+ required: false
+ description: |
+ Use the `startCursor` as a value for the `before` parameter to get the next page.
+ schema:
+ type: string
+ example: bfg23aksjf23zb1==
+ Sort:
+ name: sort
+ description: |-
+ To sort by id in descending menuItem use `-id`.
+ To sort by id in ascending menuItem use `id`.
+ in: query
+ required: false
+ schema:
+ type: string
+ example: '-name'
+ Filter:
+ name: filter
+ description: |-
+ Filters the collection items using space-separated `field:value` pairs.
+
+ **Format:** `field1:value1 field2:value2`
+
+ **Supported operators:**
+ - `field:value` - Exact match
+ - `field:value1,value2` - Match any of the comma-separated values (OR)
+ - Time ranges: Use `30d` (30 days), `7d` (7 days), `1h` (1 hour), etc.
+
+ **Examples:**
+ - `status:placed` - Filter by single status.
+ - `status:placed,completed` - Filter by multiple statuses.
+ - `status:placed createdAt:7d` - Combine multiple filters.
+ in: query
+ required: false
+ schema:
+ type: string
+ example: menuItemId:prd_01h1s5z6vf2mm1mz3hevnn9va7
+ Search:
+ name: search
+ in: query
+ description: |-
+ Performs a case-insensitive text search across relevant fields in the collection.
+
+ **Fields searched depend on the endpoint:**
+ - **Menu items:** `name`, `photoTextDescription`
+
+ Returns items where any of the searchable fields contain the search term as a substring.
+ required: false
+ schema:
+ type: string
+ example: coffee
+ Limit:
+ name: limit
+ description: |
+ Use to return a number of results per page.
+ If there is more data, use in combination with `after` to page through the data.
+ in: query
+ required: false
+ schema:
+ type: integer
+ minimum: 1
+ maximum: 100
+ default: 10
+ example: 10
+ schemas:
+ Page:
+ type: object
+ properties:
+ endCursor:
+ type:
+ - string
+ - 'null'
+ description: |-
+ Use with the `after` query parameter to load the next page of data.
+ When `null`, there is no data.
+ The cursor is opaque and internal structure is subject to change.
+ startCursor:
+ type:
+ - string
+ - 'null'
+ description: |-
+ Use with the `before` query parameter to load the previous page of data.
+ When `null`, there is no data.
+ The cursor is opaque and internal structure is subject to change.
+ hasNextPage:
+ type: boolean
+ description: Indicates if there is a next page with items.
+ hasPrevPage:
+ type: boolean
+ description: Indicates if there is a previous page with items.
+ limit:
+ type: integer
+ minimum: 1
+ maximum: 100
+ default: 10
+ description: Value showing how many items are in the page limit.
+ total:
+ type: integer
+ description: Count of items across all pages.
+ minimum: 0
+ required:
+ - endCursor
+ - startCursor
+ - hasNextPage
+ - hasPrevPage
+ - limit
+ - total
+ MenuBaseItem:
+ type: object
+ properties:
+ createdAt:
+ description: Created date.
+ type: string
+ format: date-time
+ readOnly: true
+ updatedAt:
+ description: Updated date.
+ type: string
+ format: date-time
+ readOnly: true
+ id:
+ description: Menu item ID. Unique identifier prefixed with `prd_`.
+ type: string
+ readOnly: true
+ pattern: ^prd_[0-9abcdefghjkmnpqrstvwxyz]{26}$
+ example: prd_01h1s5z6vf2mm1mz3hevnn9va7
+ object:
+ description: Entity name.
+ type: string
+ const: menuItem
+ readOnly: true
+ name:
+ description: Menu item name.
+ type: string
+ minLength: 1
+ maxLength: 50
+ price:
+ description: Price in cents.
+ type: integer
+ minimum: 0
+ photo:
+ writeOnly: true
+ type:
+ - string
+ - 'null'
+ format: binary
+ description: Photo of the menu item. Must be a PNG image and less than 1MB.
+ photoUrl:
+ readOnly: true
+ type: string
+ format: uri
+ description: Photo URL of the menu item.
+ photoTextDescription:
+ type:
+ - string
+ - 'null'
+ required:
+ - id
+ - name
+ - price
+ - createdAt
+ - updatedAt
+ - object
+ Beverage:
+ allOf:
+ - type: object
+ properties:
+ category:
+ description: Menu item category.
+ type: string
+ const: beverage
+ volume:
+ type: number
+ description: Size of the beverage in milliliters.
+ exclusiveMinimum: 0
+ containsCaffeine:
+ type: boolean
+ description: Indicates if the beverage contains caffeine.
+ - $ref: '#/components/schemas/MenuBaseItem'
+ Dessert:
+ allOf:
+ - type: object
+ properties:
+ category:
+ description: Menu item category.
+ type: string
+ const: dessert
+ calories:
+ type: number
+ exclusiveMinimum: 0
+ description: Amount of calories.
+ - $ref: '#/components/schemas/MenuBaseItem'
+ MenuItem:
+ discriminator:
+ propertyName: category
+ mapping:
+ beverage: '#/components/schemas/Beverage'
+ dessert: '#/components/schemas/Dessert'
+ oneOf:
+ - $ref: '#/components/schemas/Beverage'
+ - $ref: '#/components/schemas/Dessert'
+ required:
+ - category
+ MenuItemList:
+ type: array
+ items:
+ type: object
+ properties:
+ object:
+ type: string
+ const: list
+ description: Entity name.
+ page:
+ $ref: '#/components/schemas/Page'
+ items:
+ type: array
+ items:
+ $ref: '#/components/schemas/MenuItem'
+ required:
+ - object
+ - page
+ - items
+ Error:
+ type: object
+ properties:
+ type:
+ type: string
+ format: uri-reference
+ description: URI reference that identifies the problem type.
+ default: about:blank
+ title:
+ type: string
+ description: Short summary of the problem type.
+ status:
+ type: integer
+ format: int32
+ description: |
+ HTTP status code generated by the origin server for this occurrence of the problem.
+ minimum: 100
+ exclusiveMaximum: 600
+ instance:
+ type: string
+ format: uri-reference
+ description: |
+ URI reference that identifies the specific occurrence of the problem, e.g. by adding a fragment identifier or sub-path to the problem type.
+ May be used to locate the root of this problem in the source code.
+ example: /some/uri-reference#specific-occurrence-context
+ details:
+ description: Additional error details.
+ type: object
+ additionalProperties: true
+ required:
+ - type
+ - title
+ - status
+ responses:
+ BadRequest:
+ description: Bad request - invalid input parameters.
+ content:
+ application/problem+json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ InternalServerError:
+ description: Internal server error.
+ content:
+ application/problem+json:
+ schema:
+ $ref: '#/components/schemas/Error'
+```
+
+
+
+In this description, `GET /menu` points to the `MenuItemList` schema. The schema intentionally says that the response is an array. Later, Respect will compare that contract with the real API response and show why they do not match.
+
+### Create an Arazzo description
+
+There are several ways to create an Arazzo description:
+
+- Read the [Arazzo specification](https://spec.openapis.org/arazzo/latest.html) and write the file from scratch for full control.
+- Use the `npx @redocly/cli@latest generate-arazzo docs-data.yaml` command as a starting point. This helps you understand the Arazzo structure and see how operations can be described.
+- Use AI assistance to draft the Arazzo description, then lint the output to catch structural or syntax errors.
+
+Whichever approach you choose, validate the Arazzo file with Redocly CLI:
+
+```bash
+npx @redocly/cli@latest lint redocly-cafe-api.arazzo.yaml
+```
+
+```bash
+No configurations were provided -- using built in recommended configuration by default.
+
+validating redocly-cafe-api.arazzo.yaml...
+redocly-cafe-api.arazzo.yaml: validated in 6ms
+
+Woohoo! Your API description is valid. 🎉
+```
+
+The following simple workflow retrieves the menu items list.
+
+`redocly-cafe-api.arazzo.yaml`
+
+```yaml
+arazzo: 1.0.1
+info:
+ title: Redocly Cafe API - Products
+ version: 1.0.0
+ description: This is the API for the Redocly Cafe Products.
+sourceDescriptions:
+ - name: redocly-cafe-api
+ type: openapi
+ url: redocly-cafe-api.yaml
+
+workflows:
+ - workflowId: menu-items-workflow
+ summary: Menu Items Workflow
+ steps:
+ - stepId: get-products
+ operationId: $sourceDescriptions.redocly-cafe-api.listMenuItems
+ description: This step gets all products.
+ parameters:
+ - in: query
+ name: limit
+ value: 1
+ successCriteria:
+ - condition: $statusCode == 200
+```
+
+Key components:
+
+- `sourceDescriptions` define the connection to the OpenAPI description.
+
+``` yaml
+sourceDescriptions:
+ - name: redocly-cafe-api
+ type: openapi
+ url: redocly-cafe-api.yaml
+```
+
+- Each step uses an `operationId` to connect the workflow step to an operation in the OpenAPI description.
+
+```yaml
+operationId: $sourceDescriptions.redocly-cafe-api.listMenuItems
+```
+
+This step resolves the `listMenuItems` operation from the `redocly-cafe-api` source description, which points to the `redocly-cafe-api.yaml` file.
+
+```yaml
+/menu:
+ get:
+ tags:
+ - Products
+ summary: List all menu items
+ description: Retrieve a collection of menu items with optional filtering and pagination.
+ operationId: listMenuItems
+ security: []
+ parameters:
+ - $ref: '#/components/parameters/After'
+ - $ref: '#/components/parameters/Before'
+ - $ref: '#/components/parameters/Sort'
+ - $ref: '#/components/parameters/Filter'
+ - $ref: '#/components/parameters/Search'
+ - $ref: '#/components/parameters/Limit'
+ responses:
+ '200':
+ description: Successful operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MenuItemList'
+ '400':
+ $ref: '#/components/responses/BadRequest'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
+```
+
+This connection is important because Respect can use the OpenAPI description to verify the response status, content type, and schema.
+
+- Steps can pass parameters to the operation. In this example, the `limit` query parameter restricts the response to one menu item.
+
+```yaml
+ parameters:
+ - in: query
+ name: limit
+ value: 1
+```
+
+- Steps can also include a `successCriteria` section to define workflow-specific expectations.
+
+```yaml
+successCriteria:
+ - condition: $statusCode == 200
+```
+
+### Execute the workflow using Redocly CLI
+
+[@redocly/cli](https://www.npmjs.com/package/@redocly/cli) is an open-source tool that can execute Arazzo descriptions with the `respect` command.
+
+```bash
+npx @redocly/cli@latest respect redocly-cafe-api.arazzo.yaml
+```
+
+The execution result looks like this. The full output is useful when debugging, but the most important lines are the checks near the end of the step:
+
+- The success criteria check passes because the API returns status code `200`.
+- The status code and content type checks pass because the response matches the documented response metadata.
+- The schema check fails because the response body shape does not match the documented schema.
+
+
+Respect execution output
+
+```bash
+ Running workflow redocly-cafe-api-se-01.arazzo.yaml / menu-items-workflow
+
+ ✗ GET /menu - step get-products
+
+ Request URL: http://localhost:4096/menu?limit=1
+ Request Headers:
+ accept: application/json, application/problem+json
+
+
+ Response status code: 200
+ Response time: 17 ms
+ Response Headers:
+ access-control-allow-credentials: true
+ connection: keep-alive
+ content-length: 435
+ content-type: application/json; charset=utf-8
+ date: Wed, 29 Apr 2026 12:00:44 GMT
+ etag: W/"1b3-id35eE7Zv3L2+3iA/a3QCfYtLb0"
+ keep-alive: timeout=5
+ vary: Origin
+ x-powered-by: Express
+ Response Size: 435 bytes
+ Response Body:
+ {
+ "object": "list",
+ "page": {
+ "startCursor": "ixCALWlkOnByZF8wMDAwMDAwMDAwc2VlZHRyYW1zMDAwMDAwMAM",
+ "endCursor": "ixCALWlkOnByZF8wMDAwMDAwMDAwc2VlZHRyYW1zMDAwMDAwMAM",
+ "hasNextPage": true,
+ "hasPrevPage": false,
+ "limit": 1,
+ "total": 5
+ },
+ "items": [
+ {
+ "id": "prd_0000000000seedtrams0000000",
+ "name": "tiramisu",
+ "price": 13000,
+ "category": "dessert",
+ "createdAt": "2026-04-29T10:00:50.610Z",
+ "updatedAt": "2026-04-29T10:00:50.610Z",
+ "object": "menuItem",
+ "calories": 450
+ }
+ ]
+ }
+
+ ✓ success criteria check - $statusCode == 200
+ ✓ status code check - $statusCode in [200, 400, 500]
+ ✓ content-type check
+ ✗ schema check
+
+
+ Failed tests info:
+
+ Workflow name: menu-items-workflow
+
+ stepId - get-products
+ ✗ schema check
+
+ TYPE must be array
+
+ > 1 | {
+ | ^
+ > 2 | "object": "list",
+ | ^^^^^^^^^^^^^^^^^^^
+ > 3 | "page": {
+ | ^^^^^^^^^^^^^^^^^^^
+ > 4 | "startCursor": "ixCALWlkOnByZF8wMDAwMDAwMDAwc2VlZHRyYW1zMDAwMDAwMAM",
+ | ^^^^^^^^^^^^^^^^^^^
+ > 5 | "endCursor": "ixCALWlkOnByZF8wMDAwMDAwMDAwc2VlZHRyYW1zMDAwMDAwMAM",
+ | ^^^^^^^^^^^^^^^^^^^
+ > 6 | "hasNextPage": true,
+ | ^^^^^^^^^^^^^^^^^^^
+ > 7 | "hasPrevPage": false,
+ | ^^^^^^^^^^^^^^^^^^^
+ > 8 | "limit": 1,
+ | ^^^^^^^^^^^^^^^^^^^
+ > 9 | "total": 5
+ | ^^^^^^^^^^^^^^^^^^^
+ > 10 | },
+ | ^^^^^^^^^^^^^^^^^^^
+ > 11 | "items": [
+ | ^^^^^^^^^^^^^^^^^^^
+ > 12 | {
+ | ^^^^^^^^^^^^^^^^^^^
+ > 13 | "id": "prd_0000000000seedtrams0000000",
+ | ^^^^^^^^^^^^^^^^^^^
+ > 14 | "name": "tiramisu",
+ | ^^^^^^^^^^^^^^^^^^^
+ > 15 | "price": 13000,
+ | ^^^^^^^^^^^^^^^^^^^
+ > 16 | "category": "dessert",
+ | ^^^^^^^^^^^^^^^^^^^
+ > 17 | "createdAt": "2026-04-29T10:00:50.610Z",
+ | ^^^^^^^^^^^^^^^^^^^
+ > 18 | "updatedAt": "2026-04-29T10:00:50.610Z",
+ | ^^^^^^^^^^^^^^^^^^^
+ > 19 | "object": "menuItem",
+ | ^^^^^^^^^^^^^^^^^^^
+ > 20 | "calories": 450
+ | ^^^^^^^^^^^^^^^^^^^
+ > 21 | }
+ | ^^^^^^^^^^^^^^^^^^^
+ > 22 | ]
+ | ^^^^^^^^^^^^^^^^^^^
+ > 23 | }
+ | ^^ 👈🏽 type must be array
+
+
+ Summary for redocly-cafe-api-se-01.arazzo.yaml
+
+ Workflows: 1 failed, 1 total
+ Steps: 1 failed, 1 total
+ Checks: 3 passed, 1 failed, 4 total
+ Time: 58ms
+
+
+┌────────────────────────────────────────────────────────────────────────────┬────────────┬─────────┬─────────┬──────────┐
+│ Filename │ Workflows │ Passed │ Failed │ Warnings │
+├────────────────────────────────────────────────────────────────────────────┼────────────┼─────────┼─────────┼──────────┤
+│ x redocly-cafe-api-se-01.arazzo.yaml │ 1 │ 0 │ 1 │ - │
+└────────────────────────────────────────────────────────────────────────────┴────────────┴─────────┴─────────┴──────────┘
+
+ Tests exited with error
+```
+
+
+
+In addition to the `success criteria check` defined in the Arazzo file,
+
+```yaml
+successCriteria:
+ - condition: $statusCode == 200
+```
+Respect performed three additional checks automatically:
+
+- `status code check`
+- `content-type check`
+- `schema check`
+
+Because the workflow step is connected to an OpenAPI description, [@redocly/cli](https://www.npmjs.com/package/@redocly/cli) verifies that the API returns a documented status code, the expected content type, and a response body that matches the documented schema.
+
+### Understand the schema failure
+
+As mentioned earlier, the API description includes an intentional discrepancy. The Respect output makes that mismatch visible.
+
+The failed schema check includes this message:
+
+ `| ^^ 👈🏽 type must be array`
+
+The API response is a paginated object, but the OpenAPI description expects an array. This is the intentional discrepancy in the example.
+
+To fix it, update the `MenuItemList` schema so it matches the actual API response:
+
+`redocly-cafe-api.yaml`
+
+```yaml
+ MenuItemList:
+ type: object
+ properties:
+ object:
+ type: string
+ const: list
+ description: Entity name.
+ page:
+ $ref: '#/components/schemas/Page'
+ items:
+ type: array
+ items:
+ $ref: '#/components/schemas/MenuItem'
+ required:
+ - object
+ - page
+ - items
+```
+
+
+Run the workflow again to confirm that all checks now pass.
+
+```bash
+npx @redocly/cli@latest respect redocly-cafe-api.arazzo.yaml
+```
+
+After the schema is updated, the response body matches the documented contract. This demonstrates the core value of contract testing with Respect: when the API and its OpenAPI description drift apart, the workflow highlights the mismatch immediately.
+
+### Practical applications
+
+After you create and verify an Arazzo workflow locally, you can use it in several ways:
+
+- Include the workflow in a CI/CD pipeline to keep API documentation synchronized with actual API behavior:
+
+```bash
+# Spawn your API instance
+npm install @redocly/cli@latest -g
+redocly respect products.arazzo.yaml --verbose
+```
+
+- Automate routine API workflows for development and QA tasks.
+- Describe important application flows with Arazzo and share them with team members. Non-technical users can also use visualization tools like Replay to understand the workflows.
+
+### Summary
+
+Arazzo provides a standard way to describe API workflows declaratively. Respect uses those workflows to run API contract tests against real API behavior.
+
+With this approach, teams can maintain API test coverage, detect documentation drift earlier, and share executable API workflows across engineering, QA, and documentation teams.
\ No newline at end of file
diff --git a/learn/arazzo/practical-example-series/oauth2-authorization-02.md b/learn/arazzo/practical-example-series/oauth2-authorization-02.md
new file mode 100644
index 00000000..10a91574
--- /dev/null
+++ b/learn/arazzo/practical-example-series/oauth2-authorization-02.md
@@ -0,0 +1,935 @@
+# Respect Practical Example Series [02]
+
+## OAuth2 Authorization with Arazzo
+
+This article shows how to use Respect, powered by Arazzo workflows, for OAuth2 authorization. You will learn how to define reusable workflows in separate files, pass values between workflows, and use those values to authorize API requests.
+
+You will cover the following topics:
+
+- Practical applications of Arazzo:
+ - Creating reusable workflows with outputs.
+ - Authorizing API requests with OAuth2.
+
+### What you will build
+
+You will build three connected Arazzo workflows:
+
+- A reusable workflow that registers an OAuth2 client and exposes the returned `clientId` and `clientSecret`.
+- An authorization workflow that reuses the client registration workflow and retrieves an access token.
+- A final workflow that uses the access token with `x-security` to create a menu item through a protected API operation.
+
+### The problem
+
+In the [previous article](./api-contract-testing-01.md), you learned how to execute a simple API contract test with [Redocly Respect](https://redocly.com/respect-cli). Now, let us move to a more realistic workflow: calling protected API endpoints.
+
+Real APIs are rarely open to public access. They usually require an authentication or authorization flow before clients can read or change protected resources. To test those endpoints with Respect, the workflow must first obtain credentials or an access token, then use that authorization data in later steps.
+
+Respect supports this use case with the [`x-security` extension](https://redocly.com/docs/respect/extensions/x-security#x-security-extension), which lets a workflow provide security values for protected operations.
+
+### Prerequisites
+
+- Familiarity with the [`x-security` extension](https://redocly.com/docs/respect/extensions/x-security#x-security-extension).
+- An API described with OpenAPI. The examples use a modified version of the Redocly Cafe API.
+
+The example uses an OpenAPI description with public operations, protected menu operations, OAuth2 security schemes, and a dynamic client registration endpoint. The most important parts for this article are `/oauth2/register`, `/oauth2/token`, the protected `POST /menu` operation, and the `OAuth2` security scheme.
+
+
+OpenAPI description used in this example
+
+```yaml
+openapi: 3.1.0
+info:
+ title: Redocly Cafe
+ description: |
+ Demo API for cafe operators (not customers) to manage menus, and revenue.
+ Create API credentials and try it yourself in a realistic OpenAPI workflow.
+ version: 1.0.0
+ contact:
+ email: team@redocly.com
+ url: https://redocly.com/contact-us/
+ license:
+ name: MIT
+ url: https://opensource.org/licenses/MIT
+ termsOfService: https://redocly.com/subscription-agreement
+servers:
+ - url: https://cafe.cloud.redocly.com
+ description: Live server.
+tags:
+ - name: Authorization
+ description: Create a client to demo the API.
+ - name: Products
+ description: Operations related to products.
+paths:
+ /menu:
+ get:
+ tags:
+ - Products
+ summary: List all menu items
+ description: Retrieve a collection of menu items with optional filtering and pagination.
+ operationId: listMenuItems
+ security: []
+ parameters:
+ - $ref: '#/components/parameters/After'
+ - $ref: '#/components/parameters/Before'
+ - $ref: '#/components/parameters/Sort'
+ - $ref: '#/components/parameters/Filter'
+ - $ref: '#/components/parameters/Search'
+ - $ref: '#/components/parameters/Limit'
+ responses:
+ '200':
+ description: Successful operation.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MenuItemList'
+ '400':
+ $ref: '#/components/responses/BadRequest'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
+ post:
+ tags:
+ - Products
+ summary: Create menu item
+ description: Create a new menu item.
+ operationId: createMenuItem
+ security:
+ - OAuth2:
+ - menu:write
+ requestBody:
+ required: true
+ content:
+ multipart/form-data:
+ schema:
+ $ref: '#/components/schemas/MenuItem'
+ responses:
+ '201':
+ description: Menu item created successfully.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MenuItem'
+ examples:
+ MenuItemResponse:
+ value:
+ id: prd_01khr487f7qm7p44xn427m43vb
+ object: menuItem
+ name: coffee
+ price: 4000
+ category: beverage
+ createdAt: '2026-02-18T10:20:38.228Z'
+ updatedAt: '2026-02-18T10:20:38.228Z'
+ volume: 600
+ containsCaffeine: false
+ '400':
+ $ref: '#/components/responses/BadRequest'
+ '401':
+ $ref: '#/components/responses/Unauthorized'
+ '403':
+ $ref: '#/components/responses/Forbidden'
+ '409':
+ $ref: '#/components/responses/Conflict'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
+ /menu/{menuItemId}:
+ parameters:
+ - $ref: '#/components/parameters/MenuItemId'
+ delete:
+ tags:
+ - Products
+ summary: Delete a menu item
+ description: Delete an existing menu item.
+ operationId: deleteMenuItem
+ security:
+ - OAuth2:
+ - menu:write
+ responses:
+ '204':
+ description: Menu item deleted successfully.
+ '400':
+ $ref: '#/components/responses/BadRequest'
+ '401':
+ $ref: '#/components/responses/Unauthorized'
+ '403':
+ $ref: '#/components/responses/Forbidden'
+ '404':
+ $ref: '#/components/responses/NotFound'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
+ /oauth2/register:
+ post:
+ tags:
+ - Authorization
+ summary: Create OAuth2 client
+ description: |
+ Register a new OAuth2 client for dynamic client registration. This endpoint implements the Dynamic Client Registration Protocol (RFC 7591), using camelCase field names instead of the RFC's snake_case convention (e.g., `redirectUris` instead of `redirect_uris`, `grantTypes` instead of `grant_types`). The `name` field is required. Other fields are optional. If not provided:
+ - `redirectUris` defaults to an empty array. Note: When using the `authorization_code` grant type,
+ `redirectUris` must be provided (per RFC 7591 Section 2).
+ - `scopes` defaults to all available scopes (menu:read, menu:write) - `grantTypes` defaults to both supported grant types (authorization_code, client_credentials)
+ Returns the registered client information per RFC 7591, including:
+ - `clientId` and `clientSecret` (must be stored securely) - `clientIdIssuedAt` and `clientSecretExpiresAt` timestamps - All registered client metadata (name, redirectUris, scopes, grantTypes)
+ operationId: registerOAuth2Client
+ security: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RegisterClientObject'
+ responses:
+ '201':
+ description: OAuth2 client registered successfully.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/OAuth2Client'
+ '400':
+ $ref: '#/components/responses/BadRequest'
+ '401':
+ $ref: '#/components/responses/Unauthorized'
+ '500':
+ $ref: '#/components/responses/InternalServerError'
+components:
+ securitySchemes:
+ OAuth2:
+ type: oauth2
+ description: OAuth2 authorization for API access.
+ flows:
+ authorizationCode:
+ authorizationUrl: https://cafe.cloud.redocly.com/oauth2/authorize
+ tokenUrl: https://cafe.cloud.redocly.com/oauth2/token
+ scopes:
+ menu:read: Read access to menu items and images
+ menu:write: Write access to menu items (create, delete)
+ clientCredentials:
+ tokenUrl: https://cafe.cloud.redocly.com/oauth2/token
+ scopes:
+ menu:read: Read access to menu items and images
+ menu:write: Write access to menu items (create, delete)
+ ApiKey:
+ type: apiKey
+ name: X-API-Key
+ in: header
+ description: API key for internal operations.
+ parameters:
+ After:
+ name: after
+ in: query
+ required: false
+ description: Use the `endCursor` as a value for the `after` parameter to get the next page.
+ schema:
+ type: string
+ example: a25fgaksjf23la==
+ Before:
+ name: before
+ in: query
+ required: false
+ description: |
+ Use the `startCursor` as a value for the `before` parameter to get the next page.
+ schema:
+ type: string
+ example: bfg23aksjf23zb1==
+ Sort:
+ name: sort
+ description: |-
+ To sort by id in descending order use `-id`.
+ To sort by id in ascending order use `id`.
+ in: query
+ required: false
+ schema:
+ type: string
+ example: '-name'
+ Filter:
+ name: filter
+ description: |-
+ Filters the collection items using space-separated `field:value` pairs.
+
+ **Format:** `field1:value1 field2:value2`
+
+ **Supported operators:**
+ - `field:value` - Exact match
+ - `field:value1,value2` - Match any of the comma-separated values (OR)
+ - Time ranges: Use `30d` (30 days), `7d` (7 days), `1h` (1 hour), etc.
+
+ **Examples:**
+ - `status:placed` - Filter by single status.
+ - `status:placed,completed` - Filter by multiple statuses.
+ - `createdAt:30d` - Filter menu items created in the last 30 days.
+ - `menuItemId:prd_01h1s5z6vf2mm1mz3hevnn9va7` - Filter by specific menu item ID.
+ - `status:placed createdAt:7d` - Combine multiple filters.
+ in: query
+ required: false
+ schema:
+ type: string
+ example: menuItemId:prd_01h1s5z6vf2mm1mz3hevnn9va7
+ Search:
+ name: search
+ in: query
+ description: |-
+ Performs a case-insensitive text search across relevant fields in the collection.
+
+ **Fields searched depend on the endpoint:**
+ - **Menu items:** `name`, `photoTextDescription`
+
+ Returns items where any of the searchable fields contain the search term as a substring.
+ required: false
+ schema:
+ type: string
+ example: coffee
+ Limit:
+ name: limit
+ description: |
+ Use to return a number of results per page.
+ If there is more data, use in combination with `after` to page through the data.
+ in: query
+ required: false
+ schema:
+ type: integer
+ minimum: 1
+ maximum: 100
+ default: 10
+ example: 10
+ MenuItemId:
+ name: menuItemId
+ in: path
+ description: ID of the menu item to retrieve.
+ required: true
+ schema:
+ type: string
+ pattern: ^prd_[0-9abcdefghjkmnpqrstvwxyz]{26}$
+ example: prd_01h1s5z6vf2mm1mz3hevnn9va7
+ schemas:
+ Page:
+ type: object
+ properties:
+ endCursor:
+ type:
+ - string
+ - 'null'
+ description: |-
+ Use with the `after` query parameter to load the next page of data.
+ When `null`, there is no data.
+ The cursor is opaque and internal structure is subject to change.
+ startCursor:
+ type:
+ - string
+ - 'null'
+ description: |-
+ Use with the `before` query parameter to load the previous page of data.
+ When `null`, there is no data.
+ The cursor is opaque and internal structure is subject to change.
+ hasNextPage:
+ type: boolean
+ description: Indicates if there is a next page with items.
+ hasPrevPage:
+ type: boolean
+ description: Indicates if there is a previous page with items.
+ limit:
+ type: integer
+ minimum: 1
+ maximum: 100
+ default: 10
+ description: Value showing how many items are in the page limit.
+ total:
+ type: integer
+ description: Count of items across all pages.
+ minimum: 0
+ required:
+ - endCursor
+ - startCursor
+ - hasNextPage
+ - hasPrevPage
+ - limit
+ - total
+ MenuBaseItem:
+ type: object
+ properties:
+ createdAt:
+ description: Created date.
+ type: string
+ format: date-time
+ readOnly: true
+ updatedAt:
+ description: Updated date.
+ type: string
+ format: date-time
+ readOnly: true
+ id:
+ description: Menu item ID. Unique identifier prefixed with `prd_`.
+ type: string
+ readOnly: true
+ pattern: ^prd_[0-9abcdefghjkmnpqrstvwxyz]{26}$
+ example: prd_01h1s5z6vf2mm1mz3hevnn9va7
+ object:
+ description: Entity name.
+ type: string
+ const: menuItem
+ readOnly: true
+ name:
+ description: Menu item name.
+ type: string
+ minLength: 1
+ maxLength: 50
+ price:
+ description: Price in cents.
+ type: integer
+ minimum: 0
+ photo:
+ writeOnly: true
+ type:
+ - string
+ - 'null'
+ format: binary
+ description: Photo of the menu item. Must be a PNG image and less than 1MB.
+ photoUrl:
+ readOnly: true
+ type: string
+ format: uri
+ description: Photo URL of the menu item.
+ photoTextDescription:
+ type:
+ - string
+ - 'null'
+ required:
+ - id
+ - name
+ - price
+ - createdAt
+ - updatedAt
+ - object
+ Beverage:
+ allOf:
+ - type: object
+ properties:
+ category:
+ description: Menu item category.
+ type: string
+ const: beverage
+ volume:
+ type: number
+ description: Size of the beverage in milliliters.
+ exclusiveMinimum: 0
+ containsCaffeine:
+ type: boolean
+ description: Indicates if the beverage contains caffeine.
+ required:
+ - category
+ - volume
+ - containsCaffeine
+ - $ref: '#/components/schemas/MenuBaseItem'
+ Dessert:
+ allOf:
+ - type: object
+ properties:
+ category:
+ description: Menu item category.
+ type: string
+ const: dessert
+ calories:
+ type: number
+ exclusiveMinimum: 0
+ description: Amount of calories.
+ required:
+ - category
+ - calories
+ - $ref: '#/components/schemas/MenuBaseItem'
+ MenuItem:
+ discriminator:
+ propertyName: category
+ mapping:
+ beverage: '#/components/schemas/Beverage'
+ dessert: '#/components/schemas/Dessert'
+ oneOf:
+ - $ref: '#/components/schemas/Beverage'
+ - $ref: '#/components/schemas/Dessert'
+ required:
+ - category
+ MenuItemList:
+ type: object
+ properties:
+ object:
+ type: string
+ const: list
+ description: Entity name.
+ page:
+ $ref: '#/components/schemas/Page'
+ items:
+ type: array
+ items:
+ $ref: '#/components/schemas/MenuItem'
+ required:
+ - object
+ - page
+ - items
+ Error:
+ type: object
+ properties:
+ type:
+ type: string
+ format: uri-reference
+ description: URI reference that identifies the problem type.
+ default: about:blank
+ title:
+ type: string
+ description: Short summary of the problem type.
+ status:
+ type: integer
+ format: int32
+ description: |
+ HTTP status code generated by the origin server for this occurrence of the problem.
+ minimum: 100
+ exclusiveMaximum: 600
+ instance:
+ type: string
+ format: uri-reference
+ description: |
+ URI reference that identifies the specific occurrence of the problem, e.g. by adding a fragment identifier or sub-path to the problem type.
+ May be used to locate the root of this problem in the source code.
+ example: /some/uri-reference#specific-occurrence-context
+ details:
+ description: Additional error details.
+ type: object
+ additionalProperties: true
+ required:
+ - type
+ - title
+ - status
+ RegisterClientObject:
+ type: object
+ properties:
+ name:
+ type: string
+ description: Client name.
+ redirectUris:
+ type: array
+ items:
+ type: string
+ format: uri
+ description: List of redirect URIs (optional, defaults to empty array).
+ scopes:
+ type: array
+ items:
+ type: string
+ enum:
+ - menu:read
+ - menu:write
+ description: List of scopes.
+ grantTypes:
+ type: array
+ items:
+ type: string
+ enum:
+ - authorization_code
+ - client_credentials
+ description: List of grant types.
+ required:
+ - name
+ OAuth2Client:
+ type: object
+ description: OAuth2 client registration response. Per RFC 7591, includes the client identifier, secret, timestamps, and all registered client metadata.
+ properties:
+ clientId:
+ type: string
+ description: Client identifier issued by the authorization server.
+ clientSecret:
+ type: string
+ description: Client secret issued by the authorization server.
+ clientIdIssuedAt:
+ type: integer
+ format: int64
+ description: Time when the client_id is issued, represented as seconds since epoch (RFC7591).
+ clientSecretExpiresAt:
+ type: integer
+ format: int64
+ description: Time at which the client_secret expires, represented as seconds since epoch. 0 indicates the secret does not expire (RFC 7591).
+ name:
+ type: string
+ description: Client name (registered metadata).
+ redirectUris:
+ type: array
+ items:
+ type: string
+ format: uri
+ description: List of redirect URIs (registered metadata).
+ registrationClientUri:
+ type: string
+ format: uri
+ description: URL of the client configuration endpoint for managing this client registration (RFC 7592).
+ registrationAccessToken:
+ type: string
+ description: Access token to be used at the client configuration endpoint for managing this client registration (RFC 7592).
+ scopes:
+ type: array
+ items:
+ type: string
+ enum:
+ - menu:read
+ - menu:write
+ description: List of scopes (registered metadata).
+ grantTypes:
+ type: array
+ items:
+ type: string
+ enum:
+ - authorization_code
+ - client_credentials
+ description: List of grant types (registered metadata).
+ required:
+ - clientId
+ - clientSecret
+ - clientIdIssuedAt
+ - clientSecretExpiresAt
+ - registrationClientUri
+ - registrationAccessToken
+ responses:
+ BadRequest:
+ description: Bad request - invalid input parameters.
+ content:
+ application/problem+json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ InternalServerError:
+ description: Internal server error.
+ content:
+ application/problem+json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ Unauthorized:
+ description: Unauthorized - authorization required.
+ content:
+ application/problem+json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ Forbidden:
+ description: Forbidden - insufficient permissions.
+ content:
+ application/problem+json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ Conflict:
+ description: Conflict - entity already exists.
+ content:
+ application/problem+json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ NotFound:
+ description: Resource not found.
+ content:
+ application/problem+json:
+ schema:
+ $ref: '#/components/schemas/Error'
+```
+
+
+
+### Retrieve an access token for OAuth2
+
+The Redocly Cafe API can register a new OAuth2 client with dynamic client registration. This flow follows the [Dynamic Client Registration Protocol (RFC 7591)](https://datatracker.ietf.org/doc/html/rfc7591).
+
+Because client registration is useful in more than one workflow, describe it in a separate Arazzo file and reuse it later. This keeps the authorization details separate from the workflow that tests the protected menu operation.
+
+#### Register OAuth2 client
+
+The API description used in this article extends the previous example with a `/oauth2/register` endpoint.
+
+`redocly-cafe-api.yaml`
+
+```yaml
+/oauth2/register:
+ post:
+ tags:
+ - Authorization
+ summary: Create OAuth2 client
+ description: |
+ Register a new OAuth2 client for dynamic client registration. This endpoint implements the Dynamic Client Registration Protocol (RFC 7591), using camelCase field names instead of the RFC's snake_case convention (e.g., `redirectUris` instead of `redirect_uris`, `grantTypes` instead of `grant_types`). The `name` field is required. Other fields are optional. If not provided:
+ - `redirectUris` defaults to an empty array. Note: When using the `authorization_code` grant type,
+ `redirectUris` must be provided (per RFC 7591 Section 2).
+ - `scopes` defaults to all available scopes (menu:read, menu:write) - `grantTypes` defaults to both supported grant types (authorization_code, client_credentials)
+ Returns the registered client information per RFC 7591, including:
+ - `clientId` and `clientSecret` (must be stored securely) - `clientIdIssuedAt` and `clientSecretExpiresAt` timestamps - All registered client metadata (name, redirectUris, scopes, grantTypes)
+ operationId: registerOAuth2Client
+ security: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RegisterClientObject'
+```
+
+Create a `register-oauth2-client.arazzo.yaml` file to register an OAuth2 client.
+
+`register-oauth2-client.arazzo.yaml`
+
+```yaml
+arazzo: 1.0.1
+info:
+ title: Redocly Cafe API - Register OAuth2 Client
+ version: 1.0.0
+ description: This is the API for the Redocly Cafe OAuth2 client registration.
+sourceDescriptions:
+ - name: redocly-cafe-api
+ type: openapi
+ url: redocly-cafe-api.yaml
+
+workflows:
+ - workflowId: register-oauth2-client-workflow
+ summary: Register a new OAuth2 client
+ description: This workflow registers a new OAuth2 client.
+ steps:
+ - stepId: register-oauth2-client
+ operationId: $sourceDescriptions.redocly-cafe-api.registerOAuth2Client
+ description: This step registers a new OAuth2 client.
+ requestBody:
+ payload:
+ name: 'code'
+ redirectUris:
+ - https://cafe.cloud.redocly.com/callback
+ scopes:
+ - 'menu:read'
+ - 'menu:write'
+ grantTypes:
+ - 'client_credentials'
+ successCriteria:
+ - condition: $statusCode == 201
+ - condition: $response.body#/clientId != null
+ - condition: $response.body#/clientSecret != null
+ outputs:
+ clientId: $response.body#/clientId
+ clientSecret: $response.body#/clientSecret
+ outputs:
+ clientId: $steps.register-oauth2-client.outputs.clientId
+ clientSecret: $steps.register-oauth2-client.outputs.clientSecret
+```
+
+Key elements:
+
+- The step is connected to the OpenAPI description through `operationId`.
+
+```yaml
+operationId: $sourceDescriptions.redocly-cafe-api.registerOAuth2Client
+```
+
+- Because this is a POST request, the workflow defines a `requestBody`.
+
+```yaml
+requestBody:
+ payload:
+ name: 'code'
+ redirectUris:
+ - https://cafe.cloud.redocly.com/callback
+ scopes:
+ - 'menu:read'
+ - 'menu:write'
+ grantTypes:
+ - 'client_credentials'
+```
+
+- The step defines outputs for values that later steps or workflows need.
+
+```yaml
+outputs:
+ clientId: $response.body#/clientId
+ clientSecret: $response.body#/clientSecret
+```
+
+- Because these values must be available outside this workflow, the complete workflow also exposes the step outputs as workflow outputs. That allows another Arazzo workflow to call this workflow and read the values by name.
+
+Execute the file with Redocly CLI to inspect the API response and confirm which values are mapped to workflow outputs:
+
+```bash
+npx @redocly/cli@latest respect register-oauth2-client.arazzo.yaml --verbose
+```
+
+#### Get access token to authorize your future requests
+
+Next, create another Arazzo file called `authorization.arazzo.yaml`.
+
+This workflow gets an access token in two steps:
+
+- First, it reuses the `register-oauth2-client` workflow to retrieve a `clientId` and `clientSecret`.
+- Then it calls the token endpoint and exposes the returned access token as a workflow output.
+
+To reuse the client registration workflow, add the reusable workflow as another `sourceDescription`.
+
+```yaml
+- name: register-oauth2
+ type: arazzo
+ url: register-oauth2-client.arazzo.yaml
+```
+Then execute that workflow from a step by referencing `workflowId: $sourceDescriptions.register-oauth2.workflows.register-oauth2-client-workflow`.
+
+The second step, `authorize-with-code`, calls the `/oauth2/token` endpoint with a predefined `/callback`. In a production application, the callback is usually implemented by the client application. In this example, the Redocly Cafe API provides the callback endpoint for demonstration purposes.
+
+This step also uses the [`x-operation` extension](https://redocly.com/docs/respect/extensions/x-operation) to make a request to a URL that is not described as an operation in the OpenAPI description.
+
+`authorization.arazzo.yaml`
+
+```yaml
+arazzo: 1.0.1
+info:
+ title: Redocly Cafe API - Authorization
+ version: 1.0.0
+ description: This is the API for the Redocly Cafe OAuth2 client registration.
+sourceDescriptions:
+ - name: redocly-cafe-api
+ type: openapi
+ url: redocly-cafe-api.yaml
+ - name: register-oauth2
+ type: arazzo
+ url: register-oauth2-client.arazzo.yaml
+
+workflows:
+ - workflowId: authorize-with-code
+ summary: Authorize a new OAuth2 client
+ steps:
+ - stepId: register-oauth2-client
+ description: This step registers a new OAuth2 client.
+ workflowId: $sourceDescriptions.register-oauth2.workflows.register-oauth2-client-workflow
+ successCriteria:
+ - condition: $statusCode == 201
+ - condition: $response.body#/clientId != null
+ outputs:
+ clientId: $outputs.clientId
+ clientSecret: $outputs.clientSecret
+ - stepId: authorize-with-code
+ x-operation:
+ method: POST
+ url: https://cafe.cloud.redocly.com/oauth2/token
+ parameters:
+ - in: header
+ name: content-type
+ value: application/x-www-form-urlencoded
+ requestBody:
+ payload:
+ grant_type: authorization_code
+ redirect_uri: https://cafe.cloud.redocly.com/callback
+ client_id: $steps.register-oauth2-client.outputs.clientId
+ client_secret: $steps.register-oauth2-client.outputs.clientSecret
+ successCriteria:
+ - condition: $statusCode == 200
+ - condition: $response.body#/access_token != null
+ outputs:
+ access_token: $response.body#/access_token
+ outputs:
+ access_token_with_code: $steps.authorize-with-code.outputs.access_token
+ client_id: $steps.register-oauth2-client.outputs.clientId
+```
+
+Execute this file:
+
+```bash
+npx @redocly/cli@latest respect authorization.arazzo.yaml --verbose
+```
+
+Inspect the response body of the last step. It should contain the `access_token` that the final workflow will use:
+
+Key result from the command output:
+
+```bash
+ Response Body:
+ {
+ "access_token": "********",
+ "token_type": "Bearer",
+ "expires_in": 3600,
+ "scope": "menu:read menu:write"
+ }
+
+ ✓ success criteria check - $statusCode == 200
+ ✓ success criteria check - $response.body#/access_token != null
+```
+
+
+#### Make a POST request to the secured endpoint
+
+Now we can combine the previous workflows and call a protected endpoint that creates a menu item. The final workflow has three steps: `authorize`, `create-menu-item`, and `verify-menu-item`.
+
+The authorization workflow returns values that the final workflow passes to the [`x-security` extension](https://redocly.com/docs/respect/extensions/x-security#x-security-extension). Respect then uses those values to authorize the protected API request.
+
+The important handoff is:
+
+- The `authorize` step calls the reusable authorization workflow.
+- The `create-menu-item` step reads `access_token` and `client_id` from the `authorize` step outputs.
+- The `x-security` extension automatically constructs appropriate authorization headers, queries, or cookies based on your parameters for the protected `createMenuItem` operation.
+
+```yaml
+x-security:
+ - schemeName: OAuth2
+ values:
+ accessToken: $steps.authorize.outputs.access_token
+ clientId: $steps.authorize.outputs.client_id
+```
+
+The final workflow looks like this:
+
+`redocly-cafe-api.arazzo.yaml`
+
+```yaml
+arazzo: 1.0.1
+info:
+ title: Redocly Cafe API - Products
+ version: 1.0.0
+ description: This is the API for the Redocly Cafe Products.
+sourceDescriptions:
+ - name: redocly-cafe-api
+ type: openapi
+ url: redocly-cafe-api.yaml
+ - name: authorization
+ type: arazzo
+ url: authorization.arazzo.yaml
+
+workflows:
+ - workflowId: menu-items-workflow
+ summary: Menu Items Workflow
+ steps:
+ - stepId: authorize
+ workflowId: $sourceDescriptions.authorization.workflows.authorize-with-code
+ outputs:
+ access_token: $outputs.access_token_with_code
+ client_id: $outputs.client_id
+ - stepId: create-menu-item
+ operationId: $sourceDescriptions.redocly-cafe-api.createMenuItem
+ description: This step creates a new menu item.
+ x-security:
+ - schemeName: OAuth2
+ values:
+ accessToken: $steps.authorize.outputs.access_token
+ clientId: $steps.authorize.outputs.client_id
+ requestBody:
+ contentType: multipart/form-data
+ payload:
+ name: $faker.string.uuid()
+ description: The dessert from Italy
+ price: 4000
+ calories: 8000
+ category: 'dessert'
+ photoTextDescription: 'this is a tiramisu'
+ successCriteria:
+ - condition: $statusCode == 201
+ - condition: $response.body#/id != null
+ outputs:
+ menuItemId: $response.body#/id
+ name: $response.body#/name
+ - stepId: verify-menu-item
+ operationId: $sourceDescriptions.redocly-cafe-api.listMenuItems
+ description: This to verify created menu item
+ parameters:
+ - in: query
+ name: filter
+ value: 'name:{$steps.create-menu-item.outputs.name}'
+ successCriteria:
+ - condition: $statusCode == 200
+ - condition: $response.body#/items/0/name == $steps.create-menu-item.outputs.name
+```
+
+
+Run the workflow to confirm that the authorization step, the protected POST request, and the verification step all pass.
+
+```bash
+npx @redocly/cli@latest respect redocly-cafe-api.arazzo.yaml --verbose
+```
+
+### Summary
+
+Respect extensions make it possible to describe authorized API requests as part of an Arazzo workflow.
+
+By splitting the process into smaller workflows with outputs, you can reuse authentication and authorization steps across multiple API contract tests. This keeps complex workflows easier to maintain while still testing realistic protected API behavior.
\ No newline at end of file
diff --git a/learn/arazzo/sidebars.yaml b/learn/arazzo/sidebars.yaml
index 226c3694..a686b6e8 100644
--- a/learn/arazzo/sidebars.yaml
+++ b/learn/arazzo/sidebars.yaml
@@ -8,3 +8,7 @@
- page: ./linting-arazzo-workflows.md
- page: ./testing-arazzo-workflows.md
- page: ./documenting-multiple-apis-using-arazzo.md
+- group: Respect practical example series
+ items:
+ - page: ./practical-example-series/api-contract-testing-01.md
+ - page: ./practical-example-series/oauth2-authorization-02.md
\ No newline at end of file